diff --git a/HACKING.md b/HACKING.md
index c0767a7c..937db693 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -8,18 +8,18 @@ to setup Raspbian yourself, there are some manual steps you need to take.
## Installing the dependencies
First, make sure you have `git` installed and clone this repository in
-`~/voice-recognizer-raspi`:
+`~/AIY-projects-python`:
```shell
sudo apt-get install git
cd
-git clone https://github.com/google/aiyprojects-raspbian.git voice-recognizer-raspi
+git clone https://github.com/google/aiyprojects-raspbian.git AIY-projects-python
```
Then, install the project dependencies and setup the services:
``` shell
-cd ~/voice-recognizer-raspi
+cd ~/AIY-projects-python
scripts/install-deps.sh
sudo scripts/install-services.sh
```
@@ -31,21 +31,10 @@ on Raspbian 2017-07-05 and later. You'll also need to configure ALSA:
``` shell
sudo scripts/configure-driver.sh
-sudo reboot
-```
-
-After your Pi has rebooted with the driver enabled, run:
-
-```
-cd ~/voice-recognizer-raspi
sudo scripts/install-alsa-config.sh
-python3 checkpoints/check_audio.py
sudo reboot
```
-Don't skip running `check_audio.py` before rebooting, as it has an important
-effect on the state of ALSA, the sound architecture.
-
## Get cloud credentials
To access the cloud services you need to register a project and generate
@@ -71,7 +60,7 @@ To execute any of these scripts on the Raspberry Pi, login to it and run
(replacing the filename with the script you want to run):
``` shell
-cd ~/voice-recognizer-raspi
+cd ~/AIY-projects-python
source env/bin/activate
python3 src/assistant_library_demo.py
```
@@ -83,22 +72,3 @@ make a copy of one of the sample scripts and rename it. Then run this command:
``` shell
sudo systemctl enable voice-recognizer.service
```
-
-## Troubleshooting
-
-If you're using the Voice Kit and see an error like:
-
-``` shell
-ALSA lib pcm.c:8403:(snd_pcm_set_params) Rate doesn't match (requested 16000Hz, get 48000Hz)
-```
-
-or
-
-``` shell
-arecord: set_params:1233: Sample format non available
-Available formats:
-- S32_LE
-```
-
-Try running `rm ~/.asoundrc && sudo reboot`. For more details, [see this GitHub
-issue](https://github.com/google/aiyprojects-raspbian/issues/183).
diff --git a/Makefile b/Makefile
index 1171c39d..5b72648c 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ check:
PYTHONPATH=$$PWD/src python3 -m unittest discover tests
deploy_scripts:
- git ls-files | rsync -avz --exclude=".*" --exclude="*.desktop" --files-from - . pi@$(PI):~/AIY-voice-kit-python
+ git ls-files | rsync -avz --exclude=".*" --exclude="*.desktop" --files-from - . pi@$(PI):~/AIY-projects-python
deploy_shortcuts:
scp $(SHORTCUTS) pi@$(PI):~/Desktop
diff --git a/checkpoints/check_wifi.py b/checkpoints/check_wifi.py
index 6b3f9d16..0ae83313 100755
--- a/checkpoints/check_wifi.py
+++ b/checkpoints/check_wifi.py
@@ -34,7 +34,7 @@ def check_wifi_is_connected():
"""Check wlan0 has an IP address."""
output = subprocess.check_output(['ifconfig', 'wlan0']).decode('utf-8')
- return 'inet addr' in output
+ return 'inet ' in output
def check_can_reach_google_server():
diff --git a/requirements.txt b/requirements.txt
index 3f74d5a4..df6eb82e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
google-assistant-grpc==0.0.2
grpc-google-cloud-speech-v1beta1==0.14.0
-google-auth-oauthlib==0.1.0
+google-auth-oauthlib==0.2.0
diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh
index 0a7e6a47..0d134a52 100755
--- a/scripts/install-deps.sh
+++ b/scripts/install-deps.sh
@@ -32,6 +32,8 @@ sudo pip3 install --upgrade pip virtualenv
cd "${scripts_dir}/.."
virtualenv --system-site-packages -p python3 env
env/bin/pip install -r requirements.txt
+echo "/home/pi/AIY-projects-python/src" > \
+ /home/pi/AIY-projects-python/env/lib/python3.5/site-packages/aiy.pth
# The google-assistant-library is only available on ARMv7.
if [[ "$(uname -m)" == "armv7l" ]] ; then
diff --git a/scripts/install-services.sh b/scripts/install-services.sh
index d03303a8..e06a084f 100755
--- a/scripts/install-services.sh
+++ b/scripts/install-services.sh
@@ -27,7 +27,7 @@ cd "$(dirname "${BASH_SOURCE[0]}")/.."
repo_path="$PWD"
for service in systemd/*.service; do
- sed "s:/home/pi/voice-recognizer-raspi:${repo_path}:g" "$service" \
+ sed "s:/home/pi/AIY-projects-python:${repo_path}:g" "$service" \
> "/lib/systemd/system/$(basename "$service")"
done
diff --git a/shortcuts/check_audio.desktop b/shortcuts/check_audio.desktop
index 4f54c918..8c961fc1 100644
--- a/shortcuts/check_audio.desktop
+++ b/shortcuts/check_audio.desktop
@@ -3,5 +3,5 @@ Encoding=UTF-8
Type=Application
Name=Check audio
Comment=Check that the voiceHAT audio input and output are both working.
-Exec=/home/pi/voice-recognizer-raspi/checkpoints/check_audio.py
+Exec=/home/pi/AIY-projects-python/checkpoints/check_audio.py
Terminal=true
diff --git a/shortcuts/check_cloud.desktop b/shortcuts/check_cloud.desktop
index 0d8566e6..71f8bd7b 100644
--- a/shortcuts/check_cloud.desktop
+++ b/shortcuts/check_cloud.desktop
@@ -3,5 +3,5 @@ Encoding=UTF-8
Type=Application
Name=Check Cloud
Comment=Check that the Cloud Speech API can be used.
-Exec=/home/pi/voice-recognizer-raspi/checkpoints/check_cloud.py
+Exec=/home/pi/AIY-projects-python/env/bin/python /home/pi/AIY-projects-python/checkpoints/check_cloud.py
Terminal=true
diff --git a/shortcuts/check_wifi.desktop b/shortcuts/check_wifi.desktop
index 1c62c99c..059aaee5 100644
--- a/shortcuts/check_wifi.desktop
+++ b/shortcuts/check_wifi.desktop
@@ -3,5 +3,5 @@ Encoding=UTF-8
Type=Application
Name=Check WiFi
Comment=Check that the WiFi is working.
-Exec=/home/pi/voice-recognizer-raspi/checkpoints/check_wifi.py
+Exec=/home/pi/AIY-projects-python/checkpoints/check_wifi.py
Terminal=true
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 00000000..6a75070c
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,26 @@
+This repository contains an easy-to-use API for the AIY Voice Kit.
+You can use it to create voice commands with simple while loops - have a look at the [demos](https://github.com/google/aiyprojects-raspbian/tree/voicekit/src).
+Documentation is at the [AIY Projects site](https://aiyprojects.withgoogle.com).
+
+For guidelines on contributing, look at [CONTRIBUTING.md](CONTRIBUTING.md).
+If you're using Raspbian instead of Google's provided image, read
+[HACKING.md](HACKING.md) for information on getting started.
+
+For returning users:
+The old voice-recognizer demo remains in the [master branch](https://github.com/google/aiyprojects-raspbian/tree/master) of this project.
+The new code is in the `voicekit` branch, and is included in images starting with aiyprojects-2017-09-11.img.
+
+# Support
+
+If you're having trouble assembling your Voice Kit or running the demos,
+try the [AIY Forums](https://www.raspberrypi.org/forums/viewforum.php?f=114).
+
+If you've found a bug in the AIY Voice Kit API or demos, you can look at the
+[known issues](https://github.com/google/aiyprojects-raspbian/issues) or create
+a new one, or even fix it yourself and send us a pull request.
+
+If you've found a problem with the Assistant (for example, crashes in the
+library or incorrect responses), you can try
+[the G+ community](https://plus.google.com/communities/117537996116836200696),
+[Stack Overflow](https://stackoverflow.com/questions/tagged/google-assistant-sdk),
+or [the assistant-sdk-python repo](https://github.com/googlesamples/assistant-sdk-python/).
diff --git a/src/README.txt b/src/README.txt
new file mode 100644
index 00000000..96dc92fb
--- /dev/null
+++ b/src/README.txt
@@ -0,0 +1 @@
+See README.md
diff --git a/src/aiy/_drivers/_buzzer.py b/src/aiy/_drivers/_buzzer.py
index 333b5edb..51afdf33 100644
--- a/src/aiy/_drivers/_buzzer.py
+++ b/src/aiy/_drivers/_buzzer.py
@@ -12,54 +12,204 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Buzzer driver."""
+"""
+Simple piezo buzzer controller for the pwm-soft driver.
-import threading
+This is designed to both expose the raw controller so that the user can adjust
+frequency, period, and pulse width, and also provide a simple means for playing
+melodic sounds.
+"""
+import os
import time
-import RPi.GPIO as GPIO
+USEC = 1000000
-class Buzzer:
- """Controls the buzzer to make some noises for a certain period.
+
+def HzToPeriodUsec(freq_hz):
+ """Converts a frequency given in Hz to a period expressed in microseconds."""
+ return USEC / freq_hz
+
+
+class PWMController(object):
+ """Controller that simplifies the interface to pwm-soft Linux driver.
Simple usage:
- my_buzzer = Buzzer(channel = 22)
- my_buzzer.buzz(30)
+ from aiy._drivers._buzzer import PWMController
+ with PWMController(gpio=22) as controller:
+ controller.set_frequency(440.00)
+ time.sleep(1)
+ controller.set_frequency(0)
+
+ Note: The pwm-soft driver is a little cantankerous and weird in terms of the
+ model that it uses for controlling the PWM output. Instead of specifying a
+ period and a duty cycle percentage, this driver explicitly allows the user
+ to specify how long in microseconds to keep the GPIO high, and how long the
+ entire period is.
+
+ This can make things a little strange when it comes to changing the
+ apparent frequency of the PWM output, as simply adjusting the period time
+ while leaving the pulse time constant will produce phasing effects rather
+ than frequency shifts.
+
+ For more melodious uses, set_frequency should be enough.
"""
- def __init__(self, channel):
- GPIO.setmode(GPIO.BCM)
- GPIO.setup(channel, GPIO.OUT)
- self.pwm = GPIO.PWM(channel, 4000)
- self.buzzing = False
- self.deadline = 0
- self.lock = threading.Lock()
- self.exit = False
- self.daemon = threading.Thread(target=self._daemon, daemon=True)
- self.daemon.start()
-
- def __del__(self):
- with self.lock: # pylint: disable=E1129
- self.exit = True
- self.pwm.stop()
- self.daemon.Join()
- GPIO.cleanup(self.channel)
-
- def buzz(self, seconds):
- with self.lock:
- if not self.buzzing:
- self.pwm.start(50)
- self.buzzing = True
- print('buzz start')
- self.deadline = time.monotonic() + seconds
-
- def _daemon(self):
- while True:
- with self.lock: # pylint: disable=E1129
- if self.exit:
- return
- if self.buzzing and time.monotonic() > self.deadline:
- self.pwm.stop()
- self.buzzing = False
- print('buzz start')
- time.sleep(1)
+ PWM_SOFT_BASE_PATH = '/sys/class/pwm-soft'
+ PWM_SOFT_EXPORT_PATH = PWM_SOFT_BASE_PATH + '/export'
+ PWM_SOFT_UNEXPORT_PATH = PWM_SOFT_BASE_PATH + '/unexport'
+
+ def __init__(self, gpio):
+ """Initializes and configures the pwm-soft driver for the given GPIO.
+
+ Args:
+ gpio: the number of the GPIO to use for PWM output.
+ """
+ self.gpio = gpio
+ self.pulse_fh = None
+ self.period_fh = None
+ self.exported = False
+
+ def __enter__(self):
+ """Context manager method to automatically open up."""
+ self._export_pwm()
+ return self
+
+ def __exit__(self, *args):
+ """Context manager method to clean up."""
+ self._unexport_pwm()
+
+ def _make_pwm_path(self, pwm_number):
+ """Makes a path into the an exported PWM pin.
+
+ Args:
+ pwm_number: the number of the PWM previously exported.
+ """
+ return '%s/pwm%d' % (self.PWM_SOFT_BASE_PATH, pwm_number)
+
+ def _wait_for_access(self, path):
+ retry_count = 5
+ retry_time = 0.01
+ while not os.access(path, os.W_OK) and retry_count != 0:
+ retry_count -= 1
+ time.sleep(retry_time)
+ retry_time *= 2
+
+ if not os.access(path, os.W_OK):
+ raise IOError('Could not open %s' % path)
+
+
+ def _pwrite_int(self, path, data):
+ """Helper method to quickly write a value to a sysfs node.
+
+ Args:
+ path: string of the path to the sysfs node to write the data to.
+ data: an integer to write to the sysfs node.
+ """
+ self._wait_for_access(path)
+ with open(path, 'w') as output:
+ self._write_int(output, data)
+
+ def _write_int(self, fh, data):
+ """Helper method to write a value to a pre-opened handle.
+
+ Note: this flushes the output to disk to ensure that it actually makes
+ it to the sysfs node.
+
+ Args:
+ fh: the file handle to write to (as returned by open).
+ data: the integer to write to the file.
+ """
+ fh.write('%d\n' % data)
+ fh.flush()
+
+ def _export_pwm(self):
+ """Exports the given GPIO via the pwm-soft driver.
+
+ This writes the given GPIO number to the export sysfs node and opens two
+ file handles for later use to the period and pulse sysfs nodes inside
+ the given PWM path. If it fails, this will raise an exception.
+ """
+ try:
+ self._pwrite_int(self.PWM_SOFT_EXPORT_PATH, self.gpio)
+ except:
+ self.exported = False
+ raise
+
+ self.exported = True
+
+ period_path = self._make_pwm_path(self.gpio) + '/period'
+ try:
+ self._wait_for_access(period_path)
+ self.period_fh = open(period_path, 'w')
+ except:
+ self._unexport_pwm()
+ raise
+
+ pulse_path = self._make_pwm_path(self.gpio) + '/pulse'
+ try:
+ self._wait_for_access(pulse_path)
+ self.pulse_fh = open(pulse_path, 'w')
+ except:
+ self._unexport_pwm()
+ raise
+
+
+ def _unexport_pwm(self):
+ """Unexports the given GPIO from the pwm-soft driver.
+
+ This effectively reverses _export_pwm by closing the two file handles it
+ previously opened, and then unexporting the given gpio.
+ """
+ if self.exported:
+ if self.period_fh != None:
+ self.period_fh.close()
+
+ if self.pulse_fh != None:
+ self.pulse_fh.close()
+
+ self._pwrite_int(self.PWM_SOFT_UNEXPORT_PATH, self.gpio)
+ self.exported = False
+
+ def open(self):
+ """Opens the PWNController, exports the GPIO and gets ready to play."""
+ self._export_pwm()
+
+ def close(self):
+ """Shuts down the PWMController and unexports the GPIO."""
+ self._unexport_pwm()
+
+ def set_frequency(self, freq_hz):
+ """Sets the frequency in Hz to output.
+
+ Note: This assumes a 50% duty cycle for the PWM output to provide a nice
+ clear tone on any attached piezo buzzer. For more advanced techniques
+ and effects, please see set_period_usec and set_pulse_usec.
+
+ Args:
+ freq_hz: The frequency in Hz to output.
+ """
+ if freq_hz == 0:
+ period = 0
+ pulse = 0
+ else:
+ period = int(HzToPeriodUsec(freq_hz))
+ pulse = int(period / 2)
+
+ self._write_int(self.pulse_fh, pulse)
+ self._write_int(self.period_fh, period)
+
+ def set_pulse_usec(self, pulse_usec):
+ """Sets the pulse length in microseconds.
+
+ Args:
+ pulse_usec: how long to keep the GPIO high during the PWM period.
+ """
+ self._write_int(self.pulse_fh, pulse)
+
+ def set_period_usec(self, period_usec):
+ """Sets the period length in microseconds.
+
+ Args:
+ period_usec: how long each PWM cycle will take in microseconds.
+ """
+ self._write_int(self.period_fh, period_usec)
diff --git a/src/aiy/_drivers/_hat.py b/src/aiy/_drivers/_hat.py
new file mode 100644
index 00000000..075ac96c
--- /dev/null
+++ b/src/aiy/_drivers/_hat.py
@@ -0,0 +1,51 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utilities to identify the currently installed AIY device (if any)."""
+
+import re
+import os
+
+HAT_PATH = '/proc/device-tree/hat/'
+HAT_PRODUCT_ID_RE = re.compile('0x[0-9A-Fa-f]+')
+AIY_HATS = {
+ 1: 'Voice',
+ 2: 'Vision',
+}
+
+def _is_hat_attached():
+ return os.path.exists(HAT_PATH)
+
+def _get_hat_product():
+ with open(os.path.join(HAT_PATH, 'product')) as f:
+ return f.readline().strip()
+
+def _get_hat_product_id():
+ with open(os.path.join(HAT_PATH, 'product_id')) as f:
+ matches = HAT_PRODUCT_ID_RE.match(f.readline().strip())
+ if matches:
+ return int(matches.group(0), 16)
+
+def get_aiy_device_name():
+ if not _is_hat_attached():
+ return None
+ product = _get_hat_product()
+ if not 'AIY' in product:
+ return None
+ product_id = _get_hat_product_id()
+ if not product_id:
+ return None
+ if not product_id in AIY_HATS:
+ return None
+ return AIY_HATS[product_id]
diff --git a/src/aiy/_drivers/_led.py b/src/aiy/_drivers/_led.py
index 44951723..9578a1c2 100644
--- a/src/aiy/_drivers/_led.py
+++ b/src/aiy/_drivers/_led.py
@@ -90,7 +90,7 @@ def _animate(self):
running = self.running
if not running:
return
- if state is not None:
+ if state:
if not self._parse_state(state):
raise ValueError('unsupported state: %d' % state)
if self.iterator:
diff --git a/src/aiy/_drivers/_rgbled.py b/src/aiy/_drivers/_rgbled.py
new file mode 100644
index 00000000..37f667ed
--- /dev/null
+++ b/src/aiy/_drivers/_rgbled.py
@@ -0,0 +1,264 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""RGB LED driver for the vision bonnet."""
+
+
+class RGBLED(object):
+ """Sets the KTD2026 driver chip to show patterns with the attached RGB LED.
+
+ Simple usage:
+ from aiy._drivers._rgbled import RGBLED
+ rgbled = RGBLED()
+ rgbled.SetAnimation(color=RGBLED.BLUE, pattern=RGBLED.BLINK, rate_hz=4)
+ """
+
+ OFF = 0
+ ON = 1
+ BLINK = 2
+ BREATHE = 3
+
+ # These values use a mapping of red, green, blue. Changing the channel map
+ # will affect this.
+ RED = (0xFF, 0x00, 0x00)
+ GREEN = (0x00, 0xFF, 0x00)
+ YELLOW = (0xFF, 0xFF, 0x00)
+ BLUE = (0x00, 0x00, 0xFF)
+ PURPLE = (0xFF, 0x00, 0xFF)
+ CYAN = (0x00, 0xFF, 0xFF)
+ WHITE = (0xFF, 0xFF, 0xFF)
+
+ ENABLE_OFF = 0
+ ENABLE_ON = 1
+ ENABLE_PWM1 = 2
+ ENABLE_PWM2 = 3
+
+ DEFAULT_CHANNEL_MAP = {'red': 1, 'green': 2, 'blue': 3, 'privacy': 4}
+
+ def __init__(self, channel_map=None, debug=False):
+ """Initializes the RGB LED driver.
+
+ Args:
+ channel_map: a dictionary of name -> channel number. Determined
+ experimentally. Typically this will be red, gree, blue, with the
+ values 1, 2, 3, respectively. Defaults to the aforementioned map.
+ debug: whether or not to output what is being written raw to the
+ various sysfs nodes.
+ """
+ if channel_map is None:
+ self._channel_map = self.DEFAULT_CHANNEL_MAP
+ else:
+ self._channel_map = channel_map
+ self._debug = debug
+ self.Reset()
+
+ def __del__(self):
+ self.Reset()
+
+ def _MakeChannelPath(self, channel):
+ """Generates a ktd202x sysfs node path from a given channel name.
+
+ Args:
+ channel: a string naming the channel to select.
+ Returns:
+ A string containing the base path to the channel's LED class device
+ sysfs path.
+ """
+ return '/sys/class/leds/ktd202x:led%d/' % self._channel_map[channel]
+
+ def _PWriteInt(self, channel, filename, data):
+ """Helper method to quickly write a value to a channel sysfs node.
+
+ This is functionally equivalent to the pwrite system call, though does
+ not have the same OS semantics.
+
+ Args:
+ channel: string, the name of the channel to write to.
+ filename: string, the name of the file in the channel's sysfs directory to
+ write to.
+ data: integer, the value to write to the file.
+ """
+ path = self._MakeChannelPath(channel) + filename
+ if self._debug:
+ print('_PWriteInt(channel=%s, file=%s, data=%s)' % (channel, filename,
+ data))
+ with open(path, 'w') as output:
+ output.write('%d\n' % data)
+
+ def SetChannelMapping(self, mapping):
+ """Set the channel mapping from color to channel number.
+
+ Args:
+ mapping: dictionary of channel name (red, green, blue) to channel
+ number (1, 2, 3).
+ """
+ self._channel_map = mapping
+
+ def EnableChannel(self, channel, enable_state=ENABLE_ON):
+ """Sets the enable value for a given channel name.
+
+ Args:
+ channel: string, the name of the channel to set the enable bits for.
+ enable_state: integer, one of ENABLE_OFF, ENABLE_ON, ENABLE_PWM1, or
+ ENABLE_PWM2.
+ """
+ channel_num = self._channel_map[channel]
+ self._PWriteInt(channel, 'device/ch%d_enable' % channel_num, enable_state)
+
+ def SetBrightness(self, channel, brightness):
+ """Sets a given channel's brightness value.
+
+ Args:
+ channel: string, the name of the channel to set the brightness for.
+ brightness: integer, the brightness to set. 255 is brightest.
+ """
+ self._PWriteInt(channel, 'brightness', brightness)
+
+ def SetFlashPeriod(self, times_per_second):
+ """Sets the flash period in Hz for the whole device.
+
+ Args:
+ times_per_second: float, the frequency in Hz of how frequently to
+ flash the LEDs.
+ """
+ seconds_per_time = 1 / times_per_second
+ period = seconds_per_time * (126 / 16.38)
+ if period > 126:
+ period = 126
+ self._PWriteInt('red', 'device/tflash', period)
+
+ def SetRiseTime(self, time):
+ """Sets the rising time for the LED flashing.
+
+ Args:
+ time: the amount of time to take to do a rise. Max 15.
+ """
+ self._PWriteInt('red', 'device/trise', time)
+
+ def SetFallTime(self, time):
+ """Sets the falling time for the LED flashing.
+
+ Args:
+ time: the amount of time to take to do a fall. Max 15.
+ """
+ self._PWriteInt('red', 'device/tfall', time)
+
+ def SetPWM1Percentage(self, percentage=1):
+ """Sets the percentage of the flash period for PWM1 channels to be on.
+
+ Args:
+ percentage: float, from 0.0 to 1.0, percentage of the flash time to
+ keep the channels on.
+ """
+ self._PWriteInt('red', 'device/pwm1', int(255 * percentage))
+
+ def SetPWM2Percentage(self, percentage=1):
+ """Sets the percentage of the flash period for PWM2 channels to be on.
+
+ Args:
+ percentage: float, from 0.0 to 1.0, percentage of the flash time to
+ keep the channels on.
+ """
+ self._PWriteInt('red', 'device/pwm2', int(255 * percentage))
+
+ def SetColorMix(self, red=0, green=0, blue=0):
+ """Sets the solid color mix to display on all three channels.
+
+ Note: this will reset the chip and force it into solid color mode.
+
+ Args:
+ red: integer, max 255, the brightness of the red channel.
+ green: integer, max 255, the brightness of the green channel.
+ blue: integer, max 255, the brightness of the blue channel.
+ """
+ # self.Reset()
+ colors = {'red': red, 'green': green, 'blue': blue}
+ for channel_name, color in colors.items():
+ self.SetBrightness(channel_name, color)
+
+ def Reset(self):
+ """Forces a KTD202x chip reset.
+ """
+ self._PWriteInt('red', 'device/reset', 1)
+
+ def _SetAnimationPattern(self, pattern=BLINK, rate_hz=1):
+ """Helper function to setup the given blink pattern with the given rate.
+
+ Note: resets the chip.
+
+ Args:
+ pattern: integer, one of OFF, ON, BLINK, or BREATHE. ON is solid on,
+ BLINK is a 50% duty cycle hard blink with no ramps enabled, BREATHE
+ is a 30% duty cycle soft blink with ramps set to 5.
+ rate_hz: float, the rate in Hz of how often to blink the given
+ pattern. Irrelevant for OFF or ON.
+ """
+ self.Reset()
+ if pattern == self.ON:
+ self.SetPWM1Percentage(1)
+ elif pattern == self.BLINK:
+ self.SetFlashPeriod(rate_hz)
+ self.SetPWM1Percentage(0.5)
+ elif pattern == self.BREATHE:
+ self.SetFlashPeriod(rate_hz)
+ self.SetRiseTime(5)
+ self.SetFallTime(5)
+ self.SetPWM1Percentage(0.3)
+
+ def SetAnimation(self, color=RED, pattern=BLINK, rate_hz=1):
+ """Sets the given animation for the given color at the given rate.
+
+ Note: resets the chip.
+
+ Args:
+ color: tuple, one of RED, GREEN, YELLOW, BLUE, PURPLE, CYAN, WHITE, or
+ a tuple of three values signifying which channels to enable (1
+ enables, 0 disables) for the given flashing sequence.
+ pattern: integer, one of OFF, ON, BLINK, or BREATHE. ON is solid on,
+ BLINK is a 50% duty cycle hard blink with no ramps enabled, BREATHE
+ is a 30% duty cycle soft blink with ramps set to 5.
+ rate_hz: float, the rate in Hz of how often to blink the given
+ pattern. Irrelevant for OFF or ON.
+ """
+ self._SetAnimationPattern(pattern, rate_hz)
+ for (color, value) in zip(('red', 'green', 'blue'), color):
+ print((color, value))
+ state = self.ENABLE_OFF
+ if value > 0:
+ state = self.ENABLE_PWM1
+ self.EnableChannel(color, state)
+
+
+class PrivacyLED(RGBLED):
+ """Wrapper for LED driver to enable/disable privacy LED
+
+ Simple usage:
+ from aiy._drivers._rgbled import PrivacyLED
+ with PrivacyLED() # Illuminated on entry.
+ """
+
+ def __init__(self):
+ """Initializes the parent LED driver.
+
+ Configures PWM2 to breathe on the privacy channel.
+
+ """
+ super().__init__()
+
+ def __enter__(self):
+ """Configures the privacy channel to be fully illuminated."""
+ super().EnableChannel(channel='privacy', enable_state=RGBLED.ENABLE_ON)
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ """On exit, turn off the LED."""
+ super().EnableChannel(channel='privacy', enable_state=RGBLED.ENABLE_OFF)
diff --git a/src/aiy/_drivers/_spicomm.py b/src/aiy/_drivers/_spicomm.py
new file mode 100644
index 00000000..d0bea100
--- /dev/null
+++ b/src/aiy/_drivers/_spicomm.py
@@ -0,0 +1,143 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Python wrapper around the VisionBonnet Spicomm device node."""
+
+import array
+import fcntl
+import struct
+import sys
+
+SPICOMM_DEV = '/dev/vision_spicomm'
+
+SPICOMM_IOCTL_BASE = 0x8900
+# TODO: 0xc0100000 should be calculated properly base on structure size.
+SPICOMM_IOCTL_TRANSACT = 0xc0100000 + SPICOMM_IOCTL_BASE + 3
+
+HEADER_SIZE = 16
+PAYLOAD_SIZE = 8 * 1024 * 1024 # 8 M
+
+FLAG_ERROR = 1 << 0
+FLAG_TIMEOUT = 1 << 1
+FLAG_OVERFLOW = 1 << 2
+
+
+class SpicommError(IOError):
+ """Base class for Spicomm errors."""
+ pass
+
+
+class SpicommDevNotFoundError(SpicommError):
+ """A usable Spicomm device node not found."""
+ pass
+
+
+class SpicommOverflowError(SpicommError):
+ """Transaction buffer too small for response.
+
+ Attributes:
+ size: Number of bytes needed for the response.
+ """
+
+ def __init__(self, size):
+ self.size = size
+ super(SpicommOverflowError, self).__init__()
+
+
+class SpicommTimeoutError(SpicommError):
+ """Transaction timed out."""
+ pass
+
+
+class SpicommInternalError(SpicommError):
+ """Internal unexpected error."""
+ pass
+
+
+class Spicomm(object):
+ """VisionBonnet Spicomm wrapper.
+
+ Provides the ability to send and receive data as a transaction.
+ This means that every call to transact consists of a combined
+ send and receive step that's atomic from the calling application's
+ point of view. Multiple threads and processes can access the device
+ node concurrently using one Spicomm instance per thread.
+ Transactions are serialized in the underlying kernel driver.
+ """
+
+ def __init__(self):
+ try:
+ self._dev = open(SPICOMM_DEV, 'r+b', 0)
+ except (IOError, OSError):
+ raise SpicommDevNotFoundError
+ self._tbuf = bytearray(HEADER_SIZE + PAYLOAD_SIZE)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.close()
+
+ def close(self):
+ if self._dev:
+ self._dev.close()
+
+ def transact(self, request, timeout=10):
+ """Execute a Spicomm transaction.
+
+ The bytes in request are sent, a response is waited for and returned.
+ If the request or response is too large SpicommOverflowError is raised.
+
+ Args:
+ request: Request bytes to send.
+ timeout: How long a response will be waited for, in seconds.
+
+ Returns:
+ Bytes-like object with response data.
+
+ Raises:
+ SpicommOverflowError: Transaction buffer was too small for response.
+ The 'size' attribute contains the required size.
+ SpicommTimeoutError : Transaction timed out.
+ SpicommInternalError: Unexpected error interacting with kernel driver.
+ """
+
+ payload_len = len(request)
+ if payload_len > PAYLOAD_SIZE:
+ raise SpicommOverflowError(PAYLOAD_SIZE)
+
+ # Fill in transaction buffer.
+ self._tbuf[0:4] = struct.pack('I', 0) # flags, not currently used.
+ self._tbuf[4:8] = struct.pack('I', int(timeout * 1000)) # timeout, ms.
+ self._tbuf[8:12] = struct.pack('I', len(self._tbuf)) # total buffer size.
+ self._tbuf[12:16] = struct.pack('I', payload_len) # filled range of buffer.
+ self._tbuf[16:16 + payload_len] = request
+
+ try:
+ # Send transaction to kernel driver.
+ fcntl.ioctl(self._dev, SPICOMM_IOCTL_TRANSACT, self._tbuf)
+
+ # No exception means errno 0 and self._tbuf is now mutated.
+ _, _, _, payload_len = struct.unpack('IIII', self._tbuf[0:16])
+ return self._tbuf[16:16 + payload_len]
+ except (IOError, OSError):
+ # FLAG_ERROR is set if we actually talked to the kernel.
+ flags, _, _, payload_len = struct.unpack('IIII', self._tbuf[0:16])
+ if flags & FLAG_ERROR:
+ if flags & FLAG_TIMEOUT:
+ raise SpicommTimeoutError
+ elif flags & FLAG_OVERFLOW:
+ raise SpicommOverflowError(payload_len)
+
+ # This is unexpected.
+ raise SpicommInternalError
diff --git a/src/aiy/_drivers/_status_ui.py b/src/aiy/_drivers/_status_ui.py
index 64556729..c6be6256 100644
--- a/src/aiy/_drivers/_status_ui.py
+++ b/src/aiy/_drivers/_status_ui.py
@@ -53,7 +53,6 @@ def set_trigger_sound_wave(self, trigger_sound_wave):
"""
if not trigger_sound_wave:
self._trigger_sound_wave = None
- return
expanded_path = os.path.expanduser(trigger_sound_wave)
if os.path.exists(expanded_path):
self._trigger_sound_wave = expanded_path
diff --git a/src/aiy/_drivers/_transport.py b/src/aiy/_drivers/_transport.py
new file mode 100644
index 00000000..6fb1f92a
--- /dev/null
+++ b/src/aiy/_drivers/_transport.py
@@ -0,0 +1,91 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport to communicate with VisionBonnet board."""
+
+import logging
+import os
+import socket
+import struct
+
+from aiy._drivers import _spicomm
+
+
+class _SpiTransport(object):
+ """Communicate with VisionBonnet over SPI bus."""
+
+ def __init__(self):
+ self._spicomm = _spicomm.Spicomm()
+
+ # TODO(dkovalev): add timeout when implemented in Spicomm
+ def send(self, request):
+ return self._spicomm.transact(request)
+
+ def close(self):
+ self._spicomm.close()
+
+
+def _socket_recvall(s, size):
+ buf = b''
+ while size:
+ newbuf = s.recv(size)
+ if not newbuf:
+ return None
+ buf += newbuf
+ size -= len(newbuf)
+ return buf
+
+
+def _socket_receive_message(s):
+ buf = _socket_recvall(s, 4) # 4 bytes
+ if not buf:
+ return None
+ size = struct.unpack('!I', buf)[0]
+ return _socket_recvall(s, size)
+
+
+def _socket_send_message(s, msg):
+ s.sendall(struct.pack('!I', len(msg))) # 4 bytes
+ s.sendall(msg) # len(msg) bytes
+
+
+class _SocketTransport(object):
+ """Communicate with VisionBonnet over socket."""
+
+ def __init__(self):
+ """Open connection to the bonnet."""
+ self._client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ host = os.environ.get('VISION_BONNET_HOST', '172.28.28.10')
+ port = int(os.environ.get('VISION_BONNET_PORT', '35000'))
+ self._client.connect((host, port))
+
+ # TODO(dkovalev,weiranzhao): add timeout parameter
+ def send(self, request):
+ _socket_send_message(self._client, request)
+ return _socket_receive_message(self._client)
+
+ def close(self):
+ self._client.close()
+
+
+def _is_arm():
+ return os.uname()[4].startswith('arm')
+
+
+def make_transport():
+ if _is_arm():
+ return _SpiTransport()
+ else:
+ return _SocketTransport()
diff --git a/src/aiy/_drivers/_tts.py b/src/aiy/_drivers/_tts.py
index c223479b..a941b479 100644
--- a/src/aiy/_drivers/_tts.py
+++ b/src/aiy/_drivers/_tts.py
@@ -33,15 +33,13 @@ def create_say(player):
return functools.partial(say, player, lang=lang)
-def say(player, words, lang='en-US', volume=60, pitch=130):
+def say(player, words, lang='en-US'):
"""Say the given words with TTS.
Args:
player: To play the text-to-speech audio.
words: string to say aloud.
lang: language for the text-to-speech engine.
- volume: volume for the text-to-speech engine.
- pitch: pitch for the text-to-speech engine.
"""
try:
(fd, tts_wav) = tempfile.mkstemp(suffix='.wav', dir=TMP_DIR)
@@ -49,8 +47,7 @@ def say(player, words, lang='en-US', volume=60, pitch=130):
logger.exception('Using fallback directory for TTS output')
(fd, tts_wav) = tempfile.mkstemp(suffix='.wav')
os.close(fd)
- words = '' + words + ''
+ words = '%s' % words
try:
subprocess.call(['pico2wave', '--lang', lang, '-w', tts_wav, words])
player.play_wav(tts_wav)
diff --git a/src/aiy/audio.py b/src/aiy/audio.py
index 7f766bbd..7711acc7 100644
--- a/src/aiy/audio.py
+++ b/src/aiy/audio.py
@@ -28,8 +28,6 @@
_voicehat_recorder = None
_voicehat_player = None
_status_ui = None
-_tts_volume = 60
-_tts_pitch = 130
class _WaveDump(object):
@@ -110,24 +108,15 @@ def play_audio(audio_data):
player.play_bytes(audio_data, sample_width=AUDIO_SAMPLE_SIZE, sample_rate=AUDIO_SAMPLE_RATE_HZ)
-def say(words, lang=None, volume=None, pitch=None):
+def say(words, lang=None):
"""Says the given words in the given language with Google TTS engine.
- If lang is specified, e.g. "en-US", it will be used to say the given words.
+ If lang is specified, e.g. "en-US', it will be used to say the given words.
Otherwise, the language from aiy.i18n will be used.
- volume (optional) volume used to say the given words.
- pitch (optional) pitch to say the given words.
- Example: aiy.audio.say('This is an example', lang="en-US", volume=75, pitch=135)
- Any of the optional variables can be left out.
"""
-
if not lang:
lang = aiy.i18n.get_language_code()
- if not volume:
- volume = aiy.audio.get_tts_volume()
- if not pitch:
- pitch = aiy.audio.get_tts_pitch()
- aiy._drivers._tts.say(aiy.audio.get_player(), words, lang=lang, volume=volume, pitch=pitch)
+ aiy._drivers._tts.say(aiy.audio.get_player(), words, lang=lang)
def get_status_ui():
@@ -140,23 +129,3 @@ def get_status_ui():
if not _status_ui:
_status_ui = aiy._drivers._StatusUi()
return _status_ui
-
-
-def set_tts_volume(volume):
- global _tts_volume
- _tts_volume = volume
-
-
-def get_tts_volume():
- global _tts_volume
- return _tts_volume
-
-
-def set_tts_pitch(pitch):
- global _tts_pitch
- _tts_pitch = pitch
-
-
-def get_tts_pitch():
- global _tts_pitch
- return _tts_pitch
diff --git a/src/aiy/cloudspeech.py b/src/aiy/cloudspeech.py
index 184ed9b5..c38ea54d 100644
--- a/src/aiy/cloudspeech.py
+++ b/src/aiy/cloudspeech.py
@@ -34,51 +34,17 @@ class _CloudSpeechRecognizer(object):
def __init__(self, credentials_file):
self._request = aiy._apis._speech.CloudSpeechRequest(credentials_file)
self._recorder = aiy.audio.get_recorder()
- self._hotwords = []
def recognize(self):
"""Recognizes the user's speech and transcript it into text.
This function listens to the user's speech via the VoiceHat speaker. Then it
contacts Google CloudSpeech APIs and returns a textual transcript if possible.
- If hotword list is populated this method will only respond if hotword is said.
"""
self._request.reset()
self._request.set_endpointer_cb(self._endpointer_callback)
self._recorder.add_processor(self._request)
- text = self._request.do_request().transcript
- if self._hotwords and text:
- text = text.lower()
- loc_min = len(text)
- hotword_found = ''
- for hotword in self._hotwords:
- loc_temp = text.find(hotword)
- if loc_temp > -1 and loc_min > loc_temp:
- loc_min = loc_temp
- hotword_found = hotword
- if hotword_found:
- parse_text = text.split(hotword_found)[1]
- return parse_text.strip()
- else:
- return ''
- else:
- return '' if self._hotwords else text
-
- def expect_hotword(self, hotword_list):
- """Enables hotword detection for a selected list
- This method is optional and populates the list of hotwords
- to be used for hotword activation.
-
- For example, to create a recognizer for Google:
-
- recognizer.expect_hotword('Google')
- recognizer.expect_hotword(['Google','Raspberry Pi'])
- """
- if isinstance(hotword_list, list):
- for hotword in hotword_list:
- self._hotwords.append(hotword.lower())
- else:
- self._hotwords.append(hotword_list.lower())
+ return self._request.do_request().transcript
def expect_phrase(self, phrase):
"""Explicitly tells the engine that the phrase is more likely to appear.
diff --git a/src/aiy/test/__init__.py b/src/aiy/test/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/aiy/toneplayer.py b/src/aiy/toneplayer.py
new file mode 100644
index 00000000..73354f41
--- /dev/null
+++ b/src/aiy/toneplayer.py
@@ -0,0 +1,192 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Simple melodic music player for the piezo buzzer."""
+
+
+import time
+import re
+
+from aiy._drivers._buzzer import PWMController
+
+
+class Rest(object):
+ """Simple internal class to represent a musical rest note.
+
+ Used in part with the TonePlayer class, this object represents a period of
+ time in a song where no sound is made. End users shouldn't have to care
+ about this too much and instead focus on the music language described in the
+ TonePlayer class.
+ """
+
+ WHOLE = 1
+ HALF = 2
+ QUARTER = 4
+ EIGHTH = 8
+ SIXTEENTH = 16
+
+ def __init__(self, bpm=120, period=QUARTER):
+ self.bpm = bpm
+ self.period = period
+
+ def to_length_secs(self):
+ """Converts from musical notation to a period of time in seconds."""
+ return (self.bpm / 60.0) / self.period
+
+
+class Note(Rest):
+ """Simple internal class to represent a musical note.
+
+ Used in part with the TonePlayer class, this object represents a musical
+ note, including its name, octave, and how long it is played. End users
+ shouldn't have to care about this too much and instead focus on the music
+ language described in the TonePlayer class."""
+
+ BASE_OCTAVE = 4
+
+ def __init__(self, name, octave=BASE_OCTAVE, bpm=120, period=Rest.QUARTER):
+ super(Note, self).__init__(bpm, period)
+ self.name = name
+ self.octave = octave
+
+ def to_frequency(self, tuning=440.0):
+ """Converts from a name and octave to a frequency in Hz.
+
+ Uses the specified tuning.
+
+ Args:
+ tuning: the frequency of the natural A note, in Hz.
+ """
+
+ NOTES = 'CcDdEFfGgAaB'
+ base = NOTES.find('A')
+
+ octave_delta = self.octave - Note.BASE_OCTAVE # 0
+ octave_halfsteps = octave_delta * 12 # 0
+ offset = NOTES.find(self.name) - base # -1
+ halfsteps = octave_halfsteps + offset # -2
+ freq = tuning * (1.059463 ** halfsteps)
+
+ return freq
+
+
+class TonePlayer(object):
+ """Class to play a simplified music notation via a PWMController.
+
+ This class makes use of a very simple music notation to play simple musical
+ tones out of a PWM controlled piezo buzzer.
+
+ The language consists of notes and rests listed in an array. Rests are
+ moments in the song when no sound is produced, and are written in this way:
+
+ r
+
+ The may be one of the following five characters, or omitted:
+
+ w: whole note
+ h: half note
+ q: quarter note (the default -- if you don't specify the length, we
+ assume quarter)
+ e: eighth note
+ s: sixteenth note
+
+ So a half note rest would be written as "rh". A quarter note rest could be
+ written as "r" or "rq".
+
+ Notes are similar to rests, but take the following form:
+
+
+
+ are written using the upper and lower case letters A-G and a-g.
+ Uppercase letters are the natural notes, whereas lowercase letters are
+ shifted up one semitone (sharp). Represented on a piano keyboard, the
+ lowercase letters are the black keys. Thus, 'C' is the natural note C, whereas
+ 'c' is actually C#, the first black key to the right of the C key.
+
+ The octave is optional, but is the numbers 1-8. If omitted, the TonePlayer
+ assumes octave 4. Like the rests, the may also be omitted and uses
+ the same notation as the rest parameter. If omitted, TonePlayer
+ assumes a length of one quarter.
+
+ With this in mind, a middle C whole note could be written "C3w". Likewise, a
+ C# quarter note in the 4th octave could be written as "c" or "c4q" or "cq".
+ """
+
+ REST_RE = re.compile(r"r(?P[whqes])?")
+ NOTE_RE = re.compile(r"(?P[A-Ga-g])(?P[1-8])?(?P[whqes])?")
+
+ PERIOD_MAP = {
+ 'w': Rest.WHOLE,
+ 'h': Rest.HALF,
+ 'q': Rest.QUARTER,
+ 'e': Rest.EIGHTH,
+ 's': Rest.SIXTEENTH
+ }
+
+ def __init__(self, gpio, bpm=120, debug=False):
+ """Initializes the TonePlayer for playing on a given GPIO pin with the
+ given tempo in beats per minute.
+
+ Args:
+ gpio: the GPIO to initialize for PWM output to a piezo buzzer.
+ bpm: the tempo of the song to play in beats per minute. Defaults to
+ 120bpm (each whole note takes 1 second to play).
+ """
+ self.gpio = gpio
+ self.bpm = bpm
+ self.debug = debug
+
+ def _parse(self, array):
+ """Helper method to parse an array of notes into Notes and Rests."""
+ return [self._parse_note(x) for x in array]
+
+ def _parse_note(self, note_str):
+ """Parses a single note/rest string into its given class instance."""
+ result = TonePlayer.REST_RE.match(note_str)
+ if result != None:
+ length = TonePlayer.PERIOD_MAP[result.group('length')]
+ return Rest(self.bpm, length)
+
+ result = TonePlayer.NOTE_RE.match(note_str)
+ if result != None:
+ name = result.group('name')
+
+ octave = 4
+ if result.group('octave') != None:
+ octave = int(result.group('octave'))
+ if octave > 8:
+ octave = 8
+ if octave < 1:
+ octave = 1
+
+ length = Rest.QUARTER
+ if result.group('length') != None:
+ length = TonePlayer.PERIOD_MAP[result.group('length')]
+
+ return Note(name, octave, self.bpm, length)
+
+ raise Exception("Couldn't parse '" + str(note_str) + "'")
+
+ def play(self, *args):
+ """Plays a sequence of notes out the piezo buzzer."""
+ parsed_notes = self._parse(args)
+ with PWMController(self.gpio) as controller:
+ for note in parsed_notes:
+ if isinstance(note, Note):
+ if self.debug:
+ print(note.name + str(note.octave), '(' + str(note.to_frequency()) + ')', str(note.to_length_secs()) + 's')
+ controller.set_frequency(note.to_frequency())
+ else:
+ controller.set_frequency(0)
+ time.sleep(note.to_length_secs())
diff --git a/src/aiy/vision/__init__.py b/src/aiy/vision/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/aiy/vision/inference.py b/src/aiy/vision/inference.py
new file mode 100644
index 00000000..95fa6e77
--- /dev/null
+++ b/src/aiy/vision/inference.py
@@ -0,0 +1,270 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""VisionBonnet InferenceEngine API.
+
+Python API to communicate with the VisionBonnet from the Raspberry Pi side.
+
+It can be used to load a model, analyze local image or image from camera
+shot. It automatically unload the model once the associated object is
+deleted. See image_classification.py and object_recognition.py as examples on
+how to use this API.
+"""
+
+import logging
+from aiy._drivers._transport import make_transport
+from aiy.vision.proto import protocol_pb2
+
+
+def _tobytes(img):
+ try:
+ return img.tobytes()
+ except AttributeError:
+ return img.tostring()
+
+
+class CameraInference(object):
+ """Helper class to run camera inference."""
+
+ def __init__(self, descriptor, params=None):
+ self._engine = InferenceEngine()
+ self._key = self._engine.load_model(descriptor)
+ self._engine.start_camera_inference(self._key, params)
+
+ def run(self):
+ while True:
+ yield self._engine.camera_inference()
+
+ def close(self):
+ self._engine.stop_camera_inference()
+ self._engine.unload_model(self._key)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.close()
+
+
+class ImageInference(object):
+ """Helper class to run image inference."""
+
+ def __init__(self, descriptor):
+ self._engine = InferenceEngine()
+ self._key = self._engine.load_model(descriptor)
+
+ def run(self, image, params=None):
+ return self._engine.image_inference(self._key, image, params)
+
+ def close(self):
+ self._engine.unload_model(self._key)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.close()
+
+
+class ModelDescriptor(object):
+ """Info used by VisionBonnet to load model."""
+
+ def __init__(self, name, input_shape, input_normalizer, compute_graph):
+ """Initialzes ModelDescriptor.
+
+ Args:
+ name: string, a name used to refer the model, should not conflict
+ with existing model names.
+ input_shape: (batch, height, width, depth). For now, only batch=1 and
+ depth=3 are supported.
+ input_normalizer: (mean, stddev) to convert input image (for analysis) to
+ the same range model is
+ trained. For example, if the model is trained with [-1, 1] input. To
+ analyze an RGB image (input range [0, 255]), one needs to specify the
+ input normalizer as (128.0, 128.0).
+ compute_graph: string, converted model proto
+ """
+ self.name = name
+ self.input_shape = input_shape
+ self.input_normalizer = input_normalizer
+ self.compute_graph = compute_graph
+
+
+class InferenceEngine(object):
+ """Class to access InferenceEngine on VisionBonnet board.
+
+ Inference result has the following format:
+
+ message InferenceResult {
+ string model_name; // Name of the model to run inference on.
+ int32 width; // Input image/frame width.
+ int32 height; // Input image/frame height.
+ Rectangle window; // Window inside width x height image/frame.
+ int32 duration_ms; // Inference duration.
+ map tensors; // Output tensors.
+
+ message Frame {
+ int32 index; // Frame number.
+ int64 timestamp_us; // Frame timestamp.
+ }
+
+ Frame frame; // Frame-specific inference data.
+ }
+ """
+
+ def __init__(self):
+ self._transport = make_transport()
+ logging.info('InferenceEngine transport: %s',
+ self._transport.__class__.__name__)
+
+ def close(self):
+ self._transport.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.close()
+
+ def _communicate(self, request, debug=False):
+ """Gets response and logs messages if need to.
+
+ Args:
+ request: protocol_pb2.Request
+ debug: boolean, if True and response contains inference result, print up
+ to the first 10 elements of each returned vector.
+
+ Returns:
+ protocol_pb2.Response
+ """
+ response = protocol_pb2.Response()
+ response.ParseFromString(self._transport.send(request.SerializeToString()))
+
+ # TODO: throw exception when error happens
+ for error in response.errors:
+ if error.type == protocol_pb2.Response.Error.INFO:
+ logging.info(error.msg)
+ elif error.type == protocol_pb2.Response.Error.WARNING:
+ logging.warning(error.msg)
+ elif error.type == protocol_pb2.Response.Error.ERROR:
+ logging.error(error.msg)
+ else:
+ logging.error('Unknown error type: %d', error.type)
+
+ if debug:
+ # Print up to the first 10 elements of each returned vector.
+ for name, tensor in response.result.tensors.items():
+ data = tensor.data[0:10]
+ logging.info('First %d elements of output tensor:', len(data))
+ for index, value in enumerate(data):
+ logging.info(' %s[%d] = %f', name, index, value)
+ return response
+
+ def load_model(self, descriptor):
+ """Loads model on VisionBonnet.
+
+ Args:
+ descriptor: ModelDescriptor, meta info that defines model name,
+ where to get the model and etc.
+ Returns:
+ Model identifier.
+ """
+ logging.info('Loading model "%s"...', descriptor.name)
+
+ batch, height, width, depth = descriptor.input_shape
+ assert batch == 1, 'Only batch == 1 is currently supported'
+ assert depth == 3, 'Only depth == 3 is currently supported'
+ mean, stddev = descriptor.input_normalizer
+
+ request = protocol_pb2.Request()
+ request.load_model.model_name = descriptor.name
+ request.load_model.input_shape.batch = batch
+ request.load_model.input_shape.height = height
+ request.load_model.input_shape.width = width
+ request.load_model.input_shape.depth = depth
+ request.load_model.input_normalizer.mean = mean
+ request.load_model.input_normalizer.stddev = stddev
+ if descriptor.compute_graph:
+ request.load_model.compute_graph = descriptor.compute_graph
+
+ self._communicate(request)
+
+ return descriptor.name
+
+ def unload_model(self, model_name):
+ """Deletes model on VisionBonnet.
+
+ Args:
+ model_name: string, unique identifier used to refer a model.
+ """
+ logging.info('Unloading model "%s"...', model_name)
+
+ request = protocol_pb2.Request()
+ request.unload_model.model_name = model_name
+ self._communicate(request)
+
+ def start_camera_inference(self, model_name, params=None):
+ """Starts inference running on VisionBonnet."""
+ request = protocol_pb2.Request()
+ request.start_camera_inference.model_name = model_name
+
+ for key, value in (params or {}).items():
+ request.start_camera_inference.params[key] = str(value)
+
+ self._communicate(request)
+
+ def camera_inference(self):
+ """Returns the latest inference result from VisionBonnet."""
+ request = protocol_pb2.Request()
+ request.camera_inference.SetInParent()
+ return self._communicate(request).result
+
+ def stop_camera_inference(self):
+ """Stops inference running on VisionBonnet."""
+ request = protocol_pb2.Request()
+ request.stop_camera_inference.SetInParent()
+ self._communicate(request)
+
+ def image_inference(self, model_name, image, params=None):
+ """Runs inference on image using model (identified by model_name).
+
+ Args:
+ model_name: string, unique identifier used to refer a model.
+ image: PIL.Image,
+ params: dict, additional parameters to run inference
+
+ Returns:
+ protocol_pb2.Response
+ """
+
+ assert model_name, 'model_name must not be empty'
+ assert image.mode == 'RGB', 'Only image.mode == RGB is supported.'
+
+ logging.info('Image inference with model "%s"...', model_name)
+
+ r, g, b = image.split()
+ width, height = image.size
+
+ request = protocol_pb2.Request()
+ request.image_inference.model_name = model_name
+ request.image_inference.tensor.shape.height = height
+ request.image_inference.tensor.shape.width = width
+ request.image_inference.tensor.shape.depth = 3
+ request.image_inference.tensor.data = (
+ _tobytes(r) + _tobytes(g) + _tobytes(b))
+
+ for key, value in (params or {}).items():
+ request.image_inference.params[key] = str(value)
+
+ return self._communicate(request).result
diff --git a/src/aiy/vision/models/__init__.py b/src/aiy/vision/models/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/aiy/vision/models/face_detection.py b/src/aiy/vision/models/face_detection.py
new file mode 100644
index 00000000..d5365fdf
--- /dev/null
+++ b/src/aiy/vision/models/face_detection.py
@@ -0,0 +1,73 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""API for Face Detection."""
+
+from __future__ import division
+
+from aiy.vision.inference import ModelDescriptor
+from aiy.vision.models import utils
+
+_COMPUTE_GRAPH_NAME = 'face_detection.binaryproto'
+
+
+def _reshape(array, width):
+ assert len(array) % width == 0
+ height = len(array) // width
+ return [array[i * width:(i + 1) * width] for i in range(height)]
+
+
+class Face(object):
+ """Face detection result."""
+
+ def __init__(self, bounding_box, face_score, joy_score):
+ """Creates a new Face instance.
+
+ Args:
+ bounding_box: (x, y, width, height).
+ face_score: float, face confidence score.
+ joy_score: float, face joy score.
+ """
+ self.bounding_box = bounding_box
+ self.face_score = face_score
+ self.joy_score = joy_score
+
+ def __str__(self):
+ return 'face_score=%f, joy_score=%f, bbox=%s' % (self.face_score,
+ self.joy_score,
+ str(self.bounding_box))
+
+
+def model():
+ # Face detection model has special implementation in VisionBonnet firmware.
+ # input_shape, input_normalizer, and computate_graph params have on effect.
+ return ModelDescriptor(
+ name='FaceDetection',
+ input_shape=(1, 0, 0, 3),
+ input_normalizer=(0, 0),
+ compute_graph=utils.load_compute_graph(_COMPUTE_GRAPH_NAME))
+
+
+def get_faces(result):
+ """Retunrs list of Face objects decoded from the inference result."""
+ assert len(result.tensors) == 3
+ # TODO(dkovalev): check tensor shapes
+ bboxes = _reshape(result.tensors['bounding_boxes'].data, 4)
+ face_scores = result.tensors['face_scores'].data
+ joy_scores = result.tensors['joy_scores'].data
+ assert len(bboxes) == len(joy_scores)
+ assert len(bboxes) == len(face_scores)
+ return [
+ Face(tuple(bbox), face_score, joy_score)
+ for bbox, face_score, joy_score in zip(bboxes, face_scores, joy_scores)
+ ]
diff --git a/src/aiy/vision/models/image_classification.py b/src/aiy/vision/models/image_classification.py
new file mode 100644
index 00000000..9886fe9d
--- /dev/null
+++ b/src/aiy/vision/models/image_classification.py
@@ -0,0 +1,49 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""API for Image Classification tasks."""
+
+from aiy.vision.inference import ModelDescriptor
+from aiy.vision.models import utils
+from aiy.vision.models.image_classification_classes import CLASSES
+
+
+_COMPUTE_GRAPH_NAME = 'mobilenet_v1_160res_0.5_imagenet.binaryproto'
+
+
+def model():
+ return ModelDescriptor(
+ name='image_classification',
+ input_shape=(1, 160, 160, 3),
+ input_normalizer=(128.0, 128.0),
+ compute_graph=utils.load_compute_graph(_COMPUTE_GRAPH_NAME))
+
+
+def get_classes(result, top_k=3):
+ """Analyzes and reports what objects are in the given image.
+
+ Args:
+ result: dict of tensors, inference result
+ top_k: int, returns top_k objects in the image.
+
+ Returns:
+ A list of (string, float) tuple, represents object, prob(object) reversely
+ ordered by prob(object).
+ """
+ assert len(result.tensors) == 1
+ tensor = result.tensors['MobilenetV1/Predictions/Softmax']
+ probs, shape = tensor.data, tensor.shape
+ assert (shape.batch, shape.height, shape.width, shape.depth) == (1, 1, 1,
+ 1001)
+ pairs = sorted(enumerate(probs), key=lambda pair: pair[1], reverse=True)
+ return [('/'.join(CLASSES[index]), prob) for index, prob in pairs[0:top_k]]
diff --git a/src/aiy/vision/models/image_classification_classes.py b/src/aiy/vision/models/image_classification_classes.py
new file mode 100644
index 00000000..c3bddce8
--- /dev/null
+++ b/src/aiy/vision/models/image_classification_classes.py
@@ -0,0 +1,1076 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Predefined image classes for image classification model."""
+
+CLASSES = (
+ ('background',), # index=0
+ ('tench', 'Tinca tinca'), # index=1
+ ('goldfish', 'Carassius auratus'), # index=2
+ ('great white shark', 'white shark', 'man-eater', 'man-eating shark',
+ 'Carcharodon carcharias'), # index=3
+ ('tiger shark', 'Galeocerdo cuvieri'), # index=4
+ ('hammerhead', 'hammerhead shark'), # index=5
+ ('electric ray', 'crampfish', 'numbfish', 'torpedo'), # index=6
+ ('stingray',), # index=7
+ ('cock',), # index=8
+ ('hen',), # index=9
+ ('ostrich', 'Struthio camelus'), # index=10
+ ('brambling', 'Fringilla montifringilla'), # index=11
+ ('goldfinch', 'Carduelis carduelis'), # index=12
+ ('house finch', 'linnet', 'Carpodacus mexicanus'), # index=13
+ ('junco', 'snowbird'), # index=14
+ ('indigo bunting', 'indigo finch', 'indigo bird',
+ 'Passerina cyanea'), # index=15
+ ('robin', 'American robin', 'Turdus migratorius'), # index=16
+ ('bulbul',), # index=17
+ ('jay',), # index=18
+ ('magpie',), # index=19
+ ('chickadee',), # index=20
+ ('water ouzel', 'dipper'), # index=21
+ ('kite',), # index=22
+ ('bald eagle', 'American eagle', 'Haliaeetus leucocephalus'), # index=23
+ ('vulture',), # index=24
+ ('great grey owl', 'great gray owl', 'Strix nebulosa'), # index=25
+ ('European fire salamander', 'Salamandra salamandra'), # index=26
+ ('common newt', 'Triturus vulgaris'), # index=27
+ ('eft',), # index=28
+ ('spotted salamander', 'Ambystoma maculatum'), # index=29
+ ('axolotl', 'mud puppy', 'Ambystoma mexicanum'), # index=30
+ ('bullfrog', 'Rana catesbeiana'), # index=31
+ ('tree frog', 'tree-frog'), # index=32
+ ('tailed frog', 'bell toad', 'ribbed toad', 'tailed toad',
+ 'Ascaphus trui'), # index=33
+ ('loggerhead', 'loggerhead turtle', 'Caretta caretta'), # index=34
+ ('leatherback turtle', 'leatherback', 'leathery turtle',
+ 'Dermochelys coriacea'), # index=35
+ ('mud turtle',), # index=36
+ ('terrapin',), # index=37
+ ('box turtle', 'box tortoise'), # index=38
+ ('banded gecko',), # index=39
+ ('common iguana', 'iguana', 'Iguana iguana'), # index=40
+ ('American chameleon', 'anole', 'Anolis carolinensis'), # index=41
+ ('whiptail', 'whiptail lizard'), # index=42
+ ('agama',), # index=43
+ ('frilled lizard', 'Chlamydosaurus kingi'), # index=44
+ ('alligator lizard',), # index=45
+ ('Gila monster', 'Heloderma suspectum'), # index=46
+ ('green lizard', 'Lacerta viridis'), # index=47
+ ('African chameleon', 'Chamaeleo chamaeleon'), # index=48
+ ('Komodo dragon', 'Komodo lizard', 'dragon lizard', 'giant lizard',
+ 'Varanus komodoensis'), # index=49
+ ('African crocodile', 'Nile crocodile', 'Crocodylus niloticus'), # index=50
+ ('American alligator', 'Alligator mississipiensis'), # index=51
+ ('triceratops',), # index=52
+ ('thunder snake', 'worm snake', 'Carphophis amoenus'), # index=53
+ ('ringneck snake', 'ring-necked snake', 'ring snake'), # index=54
+ ('hognose snake', 'puff adder', 'sand viper'), # index=55
+ ('green snake', 'grass snake'), # index=56
+ ('king snake', 'kingsnake'), # index=57
+ ('garter snake', 'grass snake'), # index=58
+ ('water snake',), # index=59
+ ('vine snake',), # index=60
+ ('night snake', 'Hypsiglena torquata'), # index=61
+ ('boa constrictor', 'Constrictor constrictor'), # index=62
+ ('rock python', 'rock snake', 'Python sebae'), # index=63
+ ('Indian cobra', 'Naja naja'), # index=64
+ ('green mamba',), # index=65
+ ('sea snake',), # index=66
+ ('horned viper', 'cerastes', 'sand viper', 'horned asp',
+ 'Cerastes cornutus'), # index=67
+ ('diamondback', 'diamondback rattlesnake',
+ 'Crotalus adamanteus'), # index=68
+ ('sidewinder', 'horned rattlesnake', 'Crotalus cerastes'), # index=69
+ ('trilobite',), # index=70
+ ('harvestman', 'daddy longlegs', 'Phalangium opilio'), # index=71
+ ('scorpion',), # index=72
+ ('black and gold garden spider', 'Argiope aurantia'), # index=73
+ ('barn spider', 'Araneus cavaticus'), # index=74
+ ('garden spider', 'Aranea diademata'), # index=75
+ ('black widow', 'Latrodectus mactans'), # index=76
+ ('tarantula',), # index=77
+ ('wolf spider', 'hunting spider'), # index=78
+ ('tick',), # index=79
+ ('centipede',), # index=80
+ ('black grouse',), # index=81
+ ('ptarmigan',), # index=82
+ ('ruffed grouse', 'partridge', 'Bonasa umbellus'), # index=83
+ ('prairie chicken', 'prairie grouse', 'prairie fowl'), # index=84
+ ('peacock',), # index=85
+ ('quail',), # index=86
+ ('partridge',), # index=87
+ ('African grey', 'African gray', 'Psittacus erithacus'), # index=88
+ ('macaw',), # index=89
+ ('sulphur-crested cockatoo', 'Kakatoe galerita',
+ 'Cacatua galerita'), # index=90
+ ('lorikeet',), # index=91
+ ('coucal',), # index=92
+ ('bee eater',), # index=93
+ ('hornbill',), # index=94
+ ('hummingbird',), # index=95
+ ('jacamar',), # index=96
+ ('toucan',), # index=97
+ ('drake',), # index=98
+ ('red-breasted merganser', 'Mergus serrator'), # index=99
+ ('goose',), # index=100
+ ('black swan', 'Cygnus atratus'), # index=101
+ ('tusker',), # index=102
+ ('echidna', 'spiny anteater', 'anteater'), # index=103
+ ('platypus', 'duckbill', 'duckbilled platypus', 'duck-billed platypus',
+ 'Ornithorhynchus anatinus'), # index=104
+ ('wallaby', 'brush kangaroo'), # index=105
+ ('koala', 'koala bear', 'kangaroo bear', 'native bear',
+ 'Phascolarctos cinereus'), # index=106
+ ('wombat',), # index=107
+ ('jellyfish',), # index=108
+ ('sea anemone', 'anemone'), # index=109
+ ('brain coral',), # index=110
+ ('flatworm', 'platyhelminth'), # index=111
+ ('nematode', 'nematode worm', 'roundworm'), # index=112
+ ('conch',), # index=113
+ ('snail',), # index=114
+ ('slug',), # index=115
+ ('sea slug', 'nudibranch'), # index=116
+ ('chiton', 'coat-of-mail shell', 'sea cradle',
+ 'polyplacophore'), # index=117
+ ('chambered nautilus', 'pearly nautilus', 'nautilus'), # index=118
+ ('Dungeness crab', 'Cancer magister'), # index=119
+ ('rock crab', 'Cancer irroratus'), # index=120
+ ('fiddler crab',), # index=121
+ ('king crab', 'Alaska crab', 'Alaskan king crab', 'Alaska king crab',
+ 'Paralithodes camtschatica'), # index=122
+ ('American lobster', 'Northern lobster', 'Maine lobster',
+ 'Homarus americanus'), # index=123
+ ('spiny lobster', 'langouste', 'rock lobster', 'crawfish', 'crayfish',
+ 'sea crawfish'), # index=124
+ ('crayfish', 'crawfish', 'crawdad', 'crawdaddy'), # index=125
+ ('hermit crab',), # index=126
+ ('isopod',), # index=127
+ ('white stork', 'Ciconia ciconia'), # index=128
+ ('black stork', 'Ciconia nigra'), # index=129
+ ('spoonbill',), # index=130
+ ('flamingo',), # index=131
+ ('little blue heron', 'Egretta caerulea'), # index=132
+ ('American egret', 'great white heron', 'Egretta albus'), # index=133
+ ('bittern',), # index=134
+ ('crane',), # index=135
+ ('limpkin', 'Aramus pictus'), # index=136
+ ('European gallinule', 'Porphyrio porphyrio'), # index=137
+ ('American coot', 'marsh hen', 'mud hen', 'water hen',
+ 'Fulica americana'), # index=138
+ ('bustard',), # index=139
+ ('ruddy turnstone', 'Arenaria interpres'), # index=140
+ ('red-backed sandpiper', 'dunlin', 'Erolia alpina'), # index=141
+ ('redshank', 'Tringa totanus'), # index=142
+ ('dowitcher',), # index=143
+ ('oystercatcher', 'oyster catcher'), # index=144
+ ('pelican',), # index=145
+ ('king penguin', 'Aptenodytes patagonica'), # index=146
+ ('albatross', 'mollymawk'), # index=147
+ ('grey whale', 'gray whale', 'devilfish', 'Eschrichtius gibbosus',
+ 'Eschrichtius robustus'), # index=148
+ ('killer whale', 'killer', 'orca', 'grampus', 'sea wolf',
+ 'Orcinus orca'), # index=149
+ ('dugong', 'Dugong dugon'), # index=150
+ ('sea lion',), # index=151
+ ('Chihuahua',), # index=152
+ ('Japanese spaniel',), # index=153
+ ('Maltese dog', 'Maltese terrier', 'Maltese'), # index=154
+ ('Pekinese', 'Pekingese', 'Peke'), # index=155
+ ('Shih-Tzu',), # index=156
+ ('Blenheim spaniel',), # index=157
+ ('papillon',), # index=158
+ ('toy terrier',), # index=159
+ ('Rhodesian ridgeback',), # index=160
+ ('Afghan hound', 'Afghan'), # index=161
+ ('basset', 'basset hound'), # index=162
+ ('beagle',), # index=163
+ ('bloodhound', 'sleuthhound'), # index=164
+ ('bluetick',), # index=165
+ ('black-and-tan coonhound',), # index=166
+ ('Walker hound', 'Walker foxhound'), # index=167
+ ('English foxhound',), # index=168
+ ('redbone',), # index=169
+ ('borzoi', 'Russian wolfhound'), # index=170
+ ('Irish wolfhound',), # index=171
+ ('Italian greyhound',), # index=172
+ ('whippet',), # index=173
+ ('Ibizan hound', 'Ibizan Podenco'), # index=174
+ ('Norwegian elkhound', 'elkhound'), # index=175
+ ('otterhound', 'otter hound'), # index=176
+ ('Saluki', 'gazelle hound'), # index=177
+ ('Scottish deerhound', 'deerhound'), # index=178
+ ('Weimaraner',), # index=179
+ ('Staffordshire bullterrier', 'Staffordshire bull terrier'), # index=180
+ ('American Staffordshire terrier', 'Staffordshire terrier',
+ 'American pit bull terrier', 'pit bull terrier'), # index=181
+ ('Bedlington terrier',), # index=182
+ ('Border terrier',), # index=183
+ ('Kerry blue terrier',), # index=184
+ ('Irish terrier',), # index=185
+ ('Norfolk terrier',), # index=186
+ ('Norwich terrier',), # index=187
+ ('Yorkshire terrier',), # index=188
+ ('wire-haired fox terrier',), # index=189
+ ('Lakeland terrier',), # index=190
+ ('Sealyham terrier', 'Sealyham'), # index=191
+ ('Airedale', 'Airedale terrier'), # index=192
+ ('cairn', 'cairn terrier'), # index=193
+ ('Australian terrier',), # index=194
+ ('Dandie Dinmont', 'Dandie Dinmont terrier'), # index=195
+ ('Boston bull', 'Boston terrier'), # index=196
+ ('miniature schnauzer',), # index=197
+ ('giant schnauzer',), # index=198
+ ('standard schnauzer',), # index=199
+ ('Scotch terrier', 'Scottish terrier', 'Scottie'), # index=200
+ ('Tibetan terrier', 'chrysanthemum dog'), # index=201
+ ('silky terrier', 'Sydney silky'), # index=202
+ ('soft-coated wheaten terrier',), # index=203
+ ('West Highland white terrier',), # index=204
+ ('Lhasa', 'Lhasa apso'), # index=205
+ ('flat-coated retriever',), # index=206
+ ('curly-coated retriever',), # index=207
+ ('golden retriever',), # index=208
+ ('Labrador retriever',), # index=209
+ ('Chesapeake Bay retriever',), # index=210
+ ('German short-haired pointer',), # index=211
+ ('vizsla', 'Hungarian pointer'), # index=212
+ ('English setter',), # index=213
+ ('Irish setter', 'red setter'), # index=214
+ ('Gordon setter',), # index=215
+ ('Brittany spaniel',), # index=216
+ ('clumber', 'clumber spaniel'), # index=217
+ ('English springer', 'English springer spaniel'), # index=218
+ ('Welsh springer spaniel',), # index=219
+ ('cocker spaniel', 'English cocker spaniel', 'cocker'), # index=220
+ ('Sussex spaniel',), # index=221
+ ('Irish water spaniel',), # index=222
+ ('kuvasz',), # index=223
+ ('schipperke',), # index=224
+ ('groenendael',), # index=225
+ ('malinois',), # index=226
+ ('briard',), # index=227
+ ('kelpie',), # index=228
+ ('komondor',), # index=229
+ ('Old English sheepdog', 'bobtail'), # index=230
+ ('Shetland sheepdog', 'Shetland sheep dog', 'Shetland'), # index=231
+ ('collie',), # index=232
+ ('Border collie',), # index=233
+ ('Bouvier des Flandres', 'Bouviers des Flandres'), # index=234
+ ('Rottweiler',), # index=235
+ ('German shepherd', 'German shepherd dog', 'German police dog',
+ 'alsatian'), # index=236
+ ('Doberman', 'Doberman pinscher'), # index=237
+ ('miniature pinscher',), # index=238
+ ('Greater Swiss Mountain dog',), # index=239
+ ('Bernese mountain dog',), # index=240
+ ('Appenzeller',), # index=241
+ ('EntleBucher',), # index=242
+ ('boxer',), # index=243
+ ('bull mastiff',), # index=244
+ ('Tibetan mastiff',), # index=245
+ ('French bulldog',), # index=246
+ ('Great Dane',), # index=247
+ ('Saint Bernard', 'St Bernard'), # index=248
+ ('Eskimo dog', 'husky'), # index=249
+ ('malamute', 'malemute', 'Alaskan malamute'), # index=250
+ ('Siberian husky',), # index=251
+ ('dalmatian', 'coach dog', 'carriage dog'), # index=252
+ ('affenpinscher', 'monkey pinscher', 'monkey dog'), # index=253
+ ('basenji',), # index=254
+ ('pug', 'pug-dog'), # index=255
+ ('Leonberg',), # index=256
+ ('Newfoundland', 'Newfoundland dog'), # index=257
+ ('Great Pyrenees',), # index=258
+ ('Samoyed', 'Samoyede'), # index=259
+ ('Pomeranian',), # index=260
+ ('chow', 'chow chow'), # index=261
+ ('keeshond',), # index=262
+ ('Brabancon griffon',), # index=263
+ ('Pembroke', 'Pembroke Welsh corgi'), # index=264
+ ('Cardigan', 'Cardigan Welsh corgi'), # index=265
+ ('toy poodle',), # index=266
+ ('miniature poodle',), # index=267
+ ('standard poodle',), # index=268
+ ('Mexican hairless',), # index=269
+ ('timber wolf', 'grey wolf', 'gray wolf', 'Canis lupus'), # index=270
+ ('white wolf', 'Arctic wolf', 'Canis lupus tundrarum'), # index=271
+ ('red wolf', 'maned wolf', 'Canis rufus', 'Canis niger'), # index=272
+ ('coyote', 'prairie wolf', 'brush wolf', 'Canis latrans'), # index=273
+ ('dingo', 'warrigal', 'warragal', 'Canis dingo'), # index=274
+ ('dhole', 'Cuon alpinus'), # index=275
+ ('African hunting dog', 'hyena dog', 'Cape hunting dog',
+ 'Lycaon pictus'), # index=276
+ ('hyena', 'hyaena'), # index=277
+ ('red fox', 'Vulpes vulpes'), # index=278
+ ('kit fox', 'Vulpes macrotis'), # index=279
+ ('Arctic fox', 'white fox', 'Alopex lagopus'), # index=280
+ ('grey fox', 'gray fox', 'Urocyon cinereoargenteus'), # index=281
+ ('tabby', 'tabby cat'), # index=282
+ ('tiger cat',), # index=283
+ ('Persian cat',), # index=284
+ ('Siamese cat', 'Siamese'), # index=285
+ ('Egyptian cat',), # index=286
+ ('cougar', 'puma', 'catamount', 'mountain lion', 'painter', 'panther',
+ 'Felis concolor'), # index=287
+ ('lynx', 'catamount'), # index=288
+ ('leopard', 'Panthera pardus'), # index=289
+ ('snow leopard', 'ounce', 'Panthera uncia'), # index=290
+ ('jaguar', 'panther', 'Panthera onca', 'Felis onca'), # index=291
+ ('lion', 'king of beasts', 'Panthera leo'), # index=292
+ ('tiger', 'Panthera tigris'), # index=293
+ ('cheetah', 'chetah', 'Acinonyx jubatus'), # index=294
+ ('brown bear', 'bruin', 'Ursus arctos'), # index=295
+ ('American black bear', 'black bear', 'Ursus americanus',
+ 'Euarctos americanus'), # index=296
+ ('ice bear', 'polar bear', 'Ursus Maritimus',
+ 'Thalarctos maritimus'), # index=297
+ ('sloth bear', 'Melursus ursinus', 'Ursus ursinus'), # index=298
+ ('mongoose',), # index=299
+ ('meerkat', 'mierkat'), # index=300
+ ('tiger beetle',), # index=301
+ ('ladybug', 'ladybeetle', 'lady beetle', 'ladybird',
+ 'ladybird beetle'), # index=302
+ ('ground beetle', 'carabid beetle'), # index=303
+ ('long-horned beetle', 'longicorn', 'longicorn beetle'), # index=304
+ ('leaf beetle', 'chrysomelid'), # index=305
+ ('dung beetle',), # index=306
+ ('rhinoceros beetle',), # index=307
+ ('weevil',), # index=308
+ ('fly',), # index=309
+ ('bee',), # index=310
+ ('ant', 'emmet', 'pismire'), # index=311
+ ('grasshopper', 'hopper'), # index=312
+ ('cricket',), # index=313
+ ('walking stick', 'walkingstick', 'stick insect'), # index=314
+ ('cockroach', 'roach'), # index=315
+ ('mantis', 'mantid'), # index=316
+ ('cicada', 'cicala'), # index=317
+ ('leafhopper',), # index=318
+ ('lacewing', 'lacewing fly'), # index=319
+ ('dragonfly', 'darning needle', "devil's darning needle", 'sewing needle',
+ 'snake feeder', 'snake doctor', 'mosquito hawk',
+ 'skeeter hawk'), # index=320
+ ('damselfly',), # index=321
+ ('admiral',), # index=322
+ ('ringlet', 'ringlet butterfly'), # index=323
+ ('monarch', 'monarch butterfly', 'milkweed butterfly',
+ 'Danaus plexippus'), # index=324
+ ('cabbage butterfly',), # index=325
+ ('sulphur butterfly', 'sulfur butterfly'), # index=326
+ ('lycaenid', 'lycaenid butterfly'), # index=327
+ ('starfish', 'sea star'), # index=328
+ ('sea urchin',), # index=329
+ ('sea cucumber', 'holothurian'), # index=330
+ ('wood rabbit', 'cottontail', 'cottontail rabbit'), # index=331
+ ('hare',), # index=332
+ ('Angora', 'Angora rabbit'), # index=333
+ ('hamster',), # index=334
+ ('porcupine', 'hedgehog'), # index=335
+ ('fox squirrel', 'eastern fox squirrel', 'Sciurus niger'), # index=336
+ ('marmot',), # index=337
+ ('beaver',), # index=338
+ ('guinea pig', 'Cavia cobaya'), # index=339
+ ('sorrel',), # index=340
+ ('zebra',), # index=341
+ ('hog', 'pig', 'grunter', 'squealer', 'Sus scrofa'), # index=342
+ ('wild boar', 'boar', 'Sus scrofa'), # index=343
+ ('warthog',), # index=344
+ ('hippopotamus', 'hippo', 'river horse',
+ 'Hippopotamus amphibius'), # index=345
+ ('ox',), # index=346
+ ('water buffalo', 'water ox', 'Asiatic buffalo',
+ 'Bubalus bubalis'), # index=347
+ ('bison',), # index=348
+ ('ram', 'tup'), # index=349
+ ('bighorn', 'bighorn sheep', 'cimarron', 'Rocky Mountain bighorn',
+ 'Rocky Mountain sheep', 'Ovis canadensis'), # index=350
+ ('ibex', 'Capra ibex'), # index=351
+ ('hartebeest',), # index=352
+ ('impala', 'Aepyceros melampus'), # index=353
+ ('gazelle',), # index=354
+ ('Arabian camel', 'dromedary', 'Camelus dromedarius'), # index=355
+ ('llama',), # index=356
+ ('weasel',), # index=357
+ ('mink',), # index=358
+ ('polecat', 'fitch', 'foulmart', 'foumart',
+ 'Mustela putorius'), # index=359
+ ('black-footed ferret', 'ferret', 'Mustela nigripes'), # index=360
+ ('otter',), # index=361
+ ('skunk', 'polecat', 'wood pussy'), # index=362
+ ('badger',), # index=363
+ ('armadillo',), # index=364
+ ('three-toed sloth', 'ai', 'Bradypus tridactylus'), # index=365
+ ('orangutan', 'orang', 'orangutang', 'Pongo pygmaeus'), # index=366
+ ('gorilla', 'Gorilla gorilla'), # index=367
+ ('chimpanzee', 'chimp', 'Pan troglodytes'), # index=368
+ ('gibbon', 'Hylobates lar'), # index=369
+ ('siamang', 'Hylobates syndactylus',
+ 'Symphalangus syndactylus'), # index=370
+ ('guenon', 'guenon monkey'), # index=371
+ ('patas', 'hussar monkey', 'Erythrocebus patas'), # index=372
+ ('baboon',), # index=373
+ ('macaque',), # index=374
+ ('langur',), # index=375
+ ('colobus', 'colobus monkey'), # index=376
+ ('proboscis monkey', 'Nasalis larvatus'), # index=377
+ ('marmoset',), # index=378
+ ('capuchin', 'ringtail', 'Cebus capucinus'), # index=379
+ ('howler monkey', 'howler'), # index=380
+ ('titi', 'titi monkey'), # index=381
+ ('spider monkey', 'Ateles geoffroyi'), # index=382
+ ('squirrel monkey', 'Saimiri sciureus'), # index=383
+ ('Madagascar cat', 'ring-tailed lemur', 'Lemur catta'), # index=384
+ ('indri', 'indris', 'Indri indri', 'Indri brevicaudatus'), # index=385
+ ('Indian elephant', 'Elephas maximus'), # index=386
+ ('African elephant', 'Loxodonta africana'), # index=387
+ ('lesser panda', 'red panda', 'panda', 'bear cat', 'cat bear',
+ 'Ailurus fulgens'), # index=388
+ ('giant panda', 'panda', 'panda bear', 'coon bear',
+ 'Ailuropoda melanoleuca'), # index=389
+ ('barracouta', 'snoek'), # index=390
+ ('eel',), # index=391
+ ('coho', 'cohoe', 'coho salmon', 'blue jack', 'silver salmon',
+ 'Oncorhynchus kisutch'), # index=392
+ ('rock beauty', 'Holocanthus tricolor'), # index=393
+ ('anemone fish',), # index=394
+ ('sturgeon',), # index=395
+ ('gar', 'garfish', 'garpike', 'billfish',
+ 'Lepisosteus osseus'), # index=396
+ ('lionfish',), # index=397
+ ('puffer', 'pufferfish', 'blowfish', 'globefish'), # index=398
+ ('abacus',), # index=399
+ ('abaya',), # index=400
+ ('academic gown', 'academic robe', "judge's robe"), # index=401
+ ('accordion', 'piano accordion', 'squeeze box'), # index=402
+ ('acoustic guitar',), # index=403
+ ('aircraft carrier', 'carrier', 'flattop',
+ 'attack aircraft carrier'), # index=404
+ ('airliner',), # index=405
+ ('airship', 'dirigible'), # index=406
+ ('altar',), # index=407
+ ('ambulance',), # index=408
+ ('amphibian', 'amphibious vehicle'), # index=409
+ ('analog clock',), # index=410
+ ('apiary', 'bee house'), # index=411
+ ('apron',), # index=412
+ ('ashcan', 'trash can', 'garbage can', 'wastebin', 'ash bin', 'ash-bin',
+ 'ashbin', 'dustbin', 'trash barrel', 'trash bin'), # index=413
+ ('assault rifle', 'assault gun'), # index=414
+ ('backpack', 'back pack', 'knapsack', 'packsack', 'rucksack',
+ 'haversack'), # index=415
+ ('bakery', 'bakeshop', 'bakehouse'), # index=416
+ ('balance beam', 'beam'), # index=417
+ ('balloon',), # index=418
+ ('ballpoint', 'ballpoint pen', 'ballpen', 'Biro'), # index=419
+ ('Band Aid',), # index=420
+ ('banjo',), # index=421
+ ('bannister', 'banister', 'balustrade', 'balusters',
+ 'handrail'), # index=422
+ ('barbell',), # index=423
+ ('barber chair',), # index=424
+ ('barbershop',), # index=425
+ ('barn',), # index=426
+ ('barometer',), # index=427
+ ('barrel', 'cask'), # index=428
+ ('barrow', 'garden cart', 'lawn cart', 'wheelbarrow'), # index=429
+ ('baseball',), # index=430
+ ('basketball',), # index=431
+ ('bassinet',), # index=432
+ ('bassoon',), # index=433
+ ('bathing cap', 'swimming cap'), # index=434
+ ('bath towel',), # index=435
+ ('bathtub', 'bathing tub', 'bath', 'tub'), # index=436
+ ('beach wagon', 'station wagon', 'wagon', 'estate car', 'beach waggon',
+ 'station waggon', 'waggon'), # index=437
+ ('beacon', 'lighthouse', 'beacon light', 'pharos'), # index=438
+ ('beaker',), # index=439
+ ('bearskin', 'busby', 'shako'), # index=440
+ ('beer bottle',), # index=441
+ ('beer glass',), # index=442
+ ('bell cote', 'bell cot'), # index=443
+ ('bib',), # index=444
+ ('bicycle-built-for-two', 'tandem bicycle', 'tandem'), # index=445
+ ('bikini', 'two-piece'), # index=446
+ ('binder', 'ring-binder'), # index=447
+ ('binoculars', 'field glasses', 'opera glasses'), # index=448
+ ('birdhouse',), # index=449
+ ('boathouse',), # index=450
+ ('bobsled', 'bobsleigh', 'bob'), # index=451
+ ('bolo tie', 'bolo', 'bola tie', 'bola'), # index=452
+ ('bonnet', 'poke bonnet'), # index=453
+ ('bookcase',), # index=454
+ ('bookshop', 'bookstore', 'bookstall'), # index=455
+ ('bottlecap',), # index=456
+ ('bow',), # index=457
+ ('bow tie', 'bow-tie', 'bowtie'), # index=458
+ ('brass', 'memorial tablet', 'plaque'), # index=459
+ ('brassiere', 'bra', 'bandeau'), # index=460
+ ('breakwater', 'groin', 'groyne', 'mole', 'bulwark', 'seawall',
+ 'jetty'), # index=461
+ ('breastplate', 'aegis', 'egis'), # index=462
+ ('broom',), # index=463
+ ('bucket', 'pail'), # index=464
+ ('buckle',), # index=465
+ ('bulletproof vest',), # index=466
+ ('bullet train', 'bullet'), # index=467
+ ('butcher shop', 'meat market'), # index=468
+ ('cab', 'hack', 'taxi', 'taxicab'), # index=469
+ ('caldron', 'cauldron'), # index=470
+ ('candle', 'taper', 'wax light'), # index=471
+ ('cannon',), # index=472
+ ('canoe',), # index=473
+ ('can opener', 'tin opener'), # index=474
+ ('cardigan',), # index=475
+ ('car mirror',), # index=476
+ ('carousel', 'carrousel', 'merry-go-round', 'roundabout',
+ 'whirligig'), # index=477
+ ("carpenter's kit", 'tool kit'), # index=478
+ ('carton',), # index=479
+ ('car wheel',), # index=480
+ ('cash machine', 'cash dispenser', 'automated teller machine',
+ 'automatic teller machine', 'automated teller', 'automatic teller',
+ 'ATM'), # index=481
+ ('cassette',), # index=482
+ ('cassette player',), # index=483
+ ('castle',), # index=484
+ ('catamaran',), # index=485
+ ('CD player',), # index=486
+ ('cello', 'violoncello'), # index=487
+ ('cellular telephone', 'cellular phone', 'cellphone', 'cell',
+ 'mobile phone'), # index=488
+ ('chain',), # index=489
+ ('chainlink fence',), # index=490
+ ('chain mail', 'ring mail', 'mail', 'chain armor', 'chain armour',
+ 'ring armor', 'ring armour'), # index=491
+ ('chain saw', 'chainsaw'), # index=492
+ ('chest',), # index=493
+ ('chiffonier', 'commode'), # index=494
+ ('chime', 'bell', 'gong'), # index=495
+ ('china cabinet', 'china closet'), # index=496
+ ('Christmas stocking',), # index=497
+ ('church', 'church building'), # index=498
+ ('cinema', 'movie theater', 'movie theatre', 'movie house',
+ 'picture palace'), # index=499
+ ('cleaver', 'meat cleaver', 'chopper'), # index=500
+ ('cliff dwelling',), # index=501
+ ('cloak',), # index=502
+ ('clog', 'geta', 'patten', 'sabot'), # index=503
+ ('cocktail shaker',), # index=504
+ ('coffee mug',), # index=505
+ ('coffeepot',), # index=506
+ ('coil', 'spiral', 'volute', 'whorl', 'helix'), # index=507
+ ('combination lock',), # index=508
+ ('computer keyboard', 'keypad'), # index=509
+ ('confectionery', 'confectionary', 'candy store'), # index=510
+ ('container ship', 'containership', 'container vessel'), # index=511
+ ('convertible',), # index=512
+ ('corkscrew', 'bottle screw'), # index=513
+ ('cornet', 'horn', 'trumpet', 'trump'), # index=514
+ ('cowboy boot',), # index=515
+ ('cowboy hat', 'ten-gallon hat'), # index=516
+ ('cradle',), # index=517
+ ('crane',), # index=518
+ ('crash helmet',), # index=519
+ ('crate',), # index=520
+ ('crib', 'cot'), # index=521
+ ('Crock Pot',), # index=522
+ ('croquet ball',), # index=523
+ ('crutch',), # index=524
+ ('cuirass',), # index=525
+ ('dam', 'dike', 'dyke'), # index=526
+ ('desk',), # index=527
+ ('desktop computer',), # index=528
+ ('dial telephone', 'dial phone'), # index=529
+ ('diaper', 'nappy', 'napkin'), # index=530
+ ('digital clock',), # index=531
+ ('digital watch',), # index=532
+ ('dining table', 'board'), # index=533
+ ('dishrag', 'dishcloth'), # index=534
+ ('dishwasher', 'dish washer', 'dishwashing machine'), # index=535
+ ('disk brake', 'disc brake'), # index=536
+ ('dock', 'dockage', 'docking facility'), # index=537
+ ('dogsled', 'dog sled', 'dog sleigh'), # index=538
+ ('dome',), # index=539
+ ('doormat', 'welcome mat'), # index=540
+ ('drilling platform', 'offshore rig'), # index=541
+ ('drum', 'membranophone', 'tympan'), # index=542
+ ('drumstick',), # index=543
+ ('dumbbell',), # index=544
+ ('Dutch oven',), # index=545
+ ('electric fan', 'blower'), # index=546
+ ('electric guitar',), # index=547
+ ('electric locomotive',), # index=548
+ ('entertainment center',), # index=549
+ ('envelope',), # index=550
+ ('espresso maker',), # index=551
+ ('face powder',), # index=552
+ ('feather boa', 'boa'), # index=553
+ ('file', 'file cabinet', 'filing cabinet'), # index=554
+ ('fireboat',), # index=555
+ ('fire engine', 'fire truck'), # index=556
+ ('fire screen', 'fireguard'), # index=557
+ ('flagpole', 'flagstaff'), # index=558
+ ('flute', 'transverse flute'), # index=559
+ ('folding chair',), # index=560
+ ('football helmet',), # index=561
+ ('forklift',), # index=562
+ ('fountain',), # index=563
+ ('fountain pen',), # index=564
+ ('four-poster',), # index=565
+ ('freight car',), # index=566
+ ('French horn', 'horn'), # index=567
+ ('frying pan', 'frypan', 'skillet'), # index=568
+ ('fur coat',), # index=569
+ ('garbage truck', 'dustcart'), # index=570
+ ('gasmask', 'respirator', 'gas helmet'), # index=571
+ ('gas pump', 'gasoline pump', 'petrol pump',
+ 'island dispenser'), # index=572
+ ('goblet',), # index=573
+ ('go-kart',), # index=574
+ ('golf ball',), # index=575
+ ('golfcart', 'golf cart'), # index=576
+ ('gondola',), # index=577
+ ('gong', 'tam-tam'), # index=578
+ ('gown',), # index=579
+ ('grand piano', 'grand'), # index=580
+ ('greenhouse', 'nursery', 'glasshouse'), # index=581
+ ('grille', 'radiator grille'), # index=582
+ ('grocery store', 'grocery', 'food market', 'market'), # index=583
+ ('guillotine',), # index=584
+ ('hair slide',), # index=585
+ ('hair spray',), # index=586
+ ('half track',), # index=587
+ ('hammer',), # index=588
+ ('hamper',), # index=589
+ ('hand blower', 'blow dryer', 'blow drier', 'hair dryer',
+ 'hair drier'), # index=590
+ ('hand-held computer', 'hand-held microcomputer'), # index=591
+ ('handkerchief', 'hankie', 'hanky', 'hankey'), # index=592
+ ('hard disc', 'hard disk', 'fixed disk'), # index=593
+ ('harmonica', 'mouth organ', 'harp', 'mouth harp'), # index=594
+ ('harp',), # index=595
+ ('harvester', 'reaper'), # index=596
+ ('hatchet',), # index=597
+ ('holster',), # index=598
+ ('home theater', 'home theatre'), # index=599
+ ('honeycomb',), # index=600
+ ('hook', 'claw'), # index=601
+ ('hoopskirt', 'crinoline'), # index=602
+ ('horizontal bar', 'high bar'), # index=603
+ ('horse cart', 'horse-cart'), # index=604
+ ('hourglass',), # index=605
+ ('iPod',), # index=606
+ ('iron', 'smoothing iron'), # index=607
+ ("jack-o'-lantern",), # index=608
+ ('jean', 'blue jean', 'denim'), # index=609
+ ('jeep', 'landrover'), # index=610
+ ('jersey', 'T-shirt', 'tee shirt'), # index=611
+ ('jigsaw puzzle',), # index=612
+ ('jinrikisha', 'ricksha', 'rickshaw'), # index=613
+ ('joystick',), # index=614
+ ('kimono',), # index=615
+ ('knee pad',), # index=616
+ ('knot',), # index=617
+ ('lab coat', 'laboratory coat'), # index=618
+ ('ladle',), # index=619
+ ('lampshade', 'lamp shade'), # index=620
+ ('laptop', 'laptop computer'), # index=621
+ ('lawn mower', 'mower'), # index=622
+ ('lens cap', 'lens cover'), # index=623
+ ('letter opener', 'paper knife', 'paperknife'), # index=624
+ ('library',), # index=625
+ ('lifeboat',), # index=626
+ ('lighter', 'light', 'igniter', 'ignitor'), # index=627
+ ('limousine', 'limo'), # index=628
+ ('liner', 'ocean liner'), # index=629
+ ('lipstick', 'lip rouge'), # index=630
+ ('Loafer',), # index=631
+ ('lotion',), # index=632
+ ('loudspeaker', 'speaker', 'speaker unit', 'loudspeaker system',
+ 'speaker system'), # index=633
+ ('loupe', "jeweler's loupe"), # index=634
+ ('lumbermill', 'sawmill'), # index=635
+ ('magnetic compass',), # index=636
+ ('mailbag', 'postbag'), # index=637
+ ('mailbox', 'letter box'), # index=638
+ ('maillot',), # index=639
+ ('maillot', 'tank suit'), # index=640
+ ('manhole cover',), # index=641
+ ('maraca',), # index=642
+ ('marimba', 'xylophone'), # index=643
+ ('mask',), # index=644
+ ('matchstick',), # index=645
+ ('maypole',), # index=646
+ ('maze', 'labyrinth'), # index=647
+ ('measuring cup',), # index=648
+ ('medicine chest', 'medicine cabinet'), # index=649
+ ('megalith', 'megalithic structure'), # index=650
+ ('microphone', 'mike'), # index=651
+ ('microwave', 'microwave oven'), # index=652
+ ('military uniform',), # index=653
+ ('milk can',), # index=654
+ ('minibus',), # index=655
+ ('miniskirt', 'mini'), # index=656
+ ('minivan',), # index=657
+ ('missile',), # index=658
+ ('mitten',), # index=659
+ ('mixing bowl',), # index=660
+ ('mobile home', 'manufactured home'), # index=661
+ ('Model T',), # index=662
+ ('modem',), # index=663
+ ('monastery',), # index=664
+ ('monitor',), # index=665
+ ('moped',), # index=666
+ ('mortar',), # index=667
+ ('mortarboard',), # index=668
+ ('mosque',), # index=669
+ ('mosquito net',), # index=670
+ ('motor scooter', 'scooter'), # index=671
+ ('mountain bike', 'all-terrain bike', 'off-roader'), # index=672
+ ('mountain tent',), # index=673
+ ('mouse', 'computer mouse'), # index=674
+ ('mousetrap',), # index=675
+ ('moving van',), # index=676
+ ('muzzle',), # index=677
+ ('nail',), # index=678
+ ('neck brace',), # index=679
+ ('necklace',), # index=680
+ ('nipple',), # index=681
+ ('notebook', 'notebook computer'), # index=682
+ ('obelisk',), # index=683
+ ('oboe', 'hautboy', 'hautbois'), # index=684
+ ('ocarina', 'sweet potato'), # index=685
+ ('odometer', 'hodometer', 'mileometer', 'milometer'), # index=686
+ ('oil filter',), # index=687
+ ('organ', 'pipe organ'), # index=688
+ ('oscilloscope', 'scope', 'cathode-ray oscilloscope', 'CRO'), # index=689
+ ('overskirt',), # index=690
+ ('oxcart',), # index=691
+ ('oxygen mask',), # index=692
+ ('packet',), # index=693
+ ('paddle', 'boat paddle'), # index=694
+ ('paddlewheel', 'paddle wheel'), # index=695
+ ('padlock',), # index=696
+ ('paintbrush',), # index=697
+ ('pajama', 'pyjama', "pj's", 'jammies'), # index=698
+ ('palace',), # index=699
+ ('panpipe', 'pandean pipe', 'syrinx'), # index=700
+ ('paper towel',), # index=701
+ ('parachute', 'chute'), # index=702
+ ('parallel bars', 'bars'), # index=703
+ ('park bench',), # index=704
+ ('parking meter',), # index=705
+ ('passenger car', 'coach', 'carriage'), # index=706
+ ('patio', 'terrace'), # index=707
+ ('pay-phone', 'pay-station'), # index=708
+ ('pedestal', 'plinth', 'footstall'), # index=709
+ ('pencil box', 'pencil case'), # index=710
+ ('pencil sharpener',), # index=711
+ ('perfume', 'essence'), # index=712
+ ('Petri dish',), # index=713
+ ('photocopier',), # index=714
+ ('pick', 'plectrum', 'plectron'), # index=715
+ ('pickelhaube',), # index=716
+ ('picket fence', 'paling'), # index=717
+ ('pickup', 'pickup truck'), # index=718
+ ('pier',), # index=719
+ ('piggy bank', 'penny bank'), # index=720
+ ('pill bottle',), # index=721
+ ('pillow',), # index=722
+ ('ping-pong ball',), # index=723
+ ('pinwheel',), # index=724
+ ('pirate', 'pirate ship'), # index=725
+ ('pitcher', 'ewer'), # index=726
+ ('plane', "carpenter's plane", 'woodworking plane'), # index=727
+ ('planetarium',), # index=728
+ ('plastic bag',), # index=729
+ ('plate rack',), # index=730
+ ('plow', 'plough'), # index=731
+ ('plunger', "plumber's helper"), # index=732
+ ('Polaroid camera', 'Polaroid Land camera'), # index=733
+ ('pole',), # index=734
+ ('police van', 'police wagon', 'paddy wagon', 'patrol wagon', 'wagon',
+ 'black Maria'), # index=735
+ ('poncho',), # index=736
+ ('pool table', 'billiard table', 'snooker table'), # index=737
+ ('pop bottle', 'soda bottle'), # index=738
+ ('pot', 'flowerpot'), # index=739
+ ("potter's wheel",), # index=740
+ ('power drill',), # index=741
+ ('prayer rug', 'prayer mat'), # index=742
+ ('printer',), # index=743
+ ('prison', 'prison house'), # index=744
+ ('projectile', 'missile'), # index=745
+ ('projector',), # index=746
+ ('puck', 'hockey puck'), # index=747
+ ('punching bag', 'punch bag', 'punching ball', 'punchball'), # index=748
+ ('purse',), # index=749
+ ('quill', 'quill pen'), # index=750
+ ('quilt', 'comforter', 'comfort', 'puff'), # index=751
+ ('racer', 'race car', 'racing car'), # index=752
+ ('racket', 'racquet'), # index=753
+ ('radiator',), # index=754
+ ('radio', 'wireless'), # index=755
+ ('radio telescope', 'radio reflector'), # index=756
+ ('rain barrel',), # index=757
+ ('recreational vehicle', 'RV', 'R.V.'), # index=758
+ ('reel',), # index=759
+ ('reflex camera',), # index=760
+ ('refrigerator', 'icebox'), # index=761
+ ('remote control', 'remote'), # index=762
+ ('restaurant', 'eating house', 'eating place', 'eatery'), # index=763
+ ('revolver', 'six-gun', 'six-shooter'), # index=764
+ ('rifle',), # index=765
+ ('rocking chair', 'rocker'), # index=766
+ ('rotisserie',), # index=767
+ ('rubber eraser', 'rubber', 'pencil eraser'), # index=768
+ ('rugby ball',), # index=769
+ ('rule', 'ruler'), # index=770
+ ('running shoe',), # index=771
+ ('safe',), # index=772
+ ('safety pin',), # index=773
+ ('saltshaker', 'salt shaker'), # index=774
+ ('sandal',), # index=775
+ ('sarong',), # index=776
+ ('sax', 'saxophone'), # index=777
+ ('scabbard',), # index=778
+ ('scale', 'weighing machine'), # index=779
+ ('school bus',), # index=780
+ ('schooner',), # index=781
+ ('scoreboard',), # index=782
+ ('screen', 'CRT screen'), # index=783
+ ('screw',), # index=784
+ ('screwdriver',), # index=785
+ ('seat belt', 'seatbelt'), # index=786
+ ('sewing machine',), # index=787
+ ('shield', 'buckler'), # index=788
+ ('shoe shop', 'shoe-shop', 'shoe store'), # index=789
+ ('shoji',), # index=790
+ ('shopping basket',), # index=791
+ ('shopping cart',), # index=792
+ ('shovel',), # index=793
+ ('shower cap',), # index=794
+ ('shower curtain',), # index=795
+ ('ski',), # index=796
+ ('ski mask',), # index=797
+ ('sleeping bag',), # index=798
+ ('slide rule', 'slipstick'), # index=799
+ ('sliding door',), # index=800
+ ('slot', 'one-armed bandit'), # index=801
+ ('snorkel',), # index=802
+ ('snowmobile',), # index=803
+ ('snowplow', 'snowplough'), # index=804
+ ('soap dispenser',), # index=805
+ ('soccer ball',), # index=806
+ ('sock',), # index=807
+ ('solar dish', 'solar collector', 'solar furnace'), # index=808
+ ('sombrero',), # index=809
+ ('soup bowl',), # index=810
+ ('space bar',), # index=811
+ ('space heater',), # index=812
+ ('space shuttle',), # index=813
+ ('spatula',), # index=814
+ ('speedboat',), # index=815
+ ('spider web', "spider's web"), # index=816
+ ('spindle',), # index=817
+ ('sports car', 'sport car'), # index=818
+ ('spotlight', 'spot'), # index=819
+ ('stage',), # index=820
+ ('steam locomotive',), # index=821
+ ('steel arch bridge',), # index=822
+ ('steel drum',), # index=823
+ ('stethoscope',), # index=824
+ ('stole',), # index=825
+ ('stone wall',), # index=826
+ ('stopwatch', 'stop watch'), # index=827
+ ('stove',), # index=828
+ ('strainer',), # index=829
+ ('streetcar', 'tram', 'tramcar', 'trolley', 'trolley car'), # index=830
+ ('stretcher',), # index=831
+ ('studio couch', 'day bed'), # index=832
+ ('stupa', 'tope'), # index=833
+ ('submarine', 'pigboat', 'sub', 'U-boat'), # index=834
+ ('suit', 'suit of clothes'), # index=835
+ ('sundial',), # index=836
+ ('sunglass',), # index=837
+ ('sunglasses', 'dark glasses', 'shades'), # index=838
+ ('sunscreen', 'sunblock', 'sun blocker'), # index=839
+ ('suspension bridge',), # index=840
+ ('swab', 'swob', 'mop'), # index=841
+ ('sweatshirt',), # index=842
+ ('swimming trunks', 'bathing trunks'), # index=843
+ ('swing',), # index=844
+ ('switch', 'electric switch', 'electrical switch'), # index=845
+ ('syringe',), # index=846
+ ('table lamp',), # index=847
+ ('tank', 'army tank', 'armored combat vehicle',
+ 'armoured combat vehicle'), # index=848
+ ('tape player',), # index=849
+ ('teapot',), # index=850
+ ('teddy', 'teddy bear'), # index=851
+ ('television', 'television system'), # index=852
+ ('tennis ball',), # index=853
+ ('thatch', 'thatched roof'), # index=854
+ ('theater curtain', 'theatre curtain'), # index=855
+ ('thimble',), # index=856
+ ('thresher', 'thrasher', 'threshing machine'), # index=857
+ ('throne',), # index=858
+ ('tile roof',), # index=859
+ ('toaster',), # index=860
+ ('tobacco shop', 'tobacconist shop', 'tobacconist'), # index=861
+ ('toilet seat',), # index=862
+ ('torch',), # index=863
+ ('totem pole',), # index=864
+ ('tow truck', 'tow car', 'wrecker'), # index=865
+ ('toyshop',), # index=866
+ ('tractor',), # index=867
+ ('trailer truck', 'tractor trailer', 'trucking rig', 'rig',
+ 'articulated lorry', 'semi'), # index=868
+ ('tray',), # index=869
+ ('trench coat',), # index=870
+ ('tricycle', 'trike', 'velocipede'), # index=871
+ ('trimaran',), # index=872
+ ('tripod',), # index=873
+ ('triumphal arch',), # index=874
+ ('trolleybus', 'trolley coach', 'trackless trolley'), # index=875
+ ('trombone',), # index=876
+ ('tub', 'vat'), # index=877
+ ('turnstile',), # index=878
+ ('typewriter keyboard',), # index=879
+ ('umbrella',), # index=880
+ ('unicycle', 'monocycle'), # index=881
+ ('upright', 'upright piano'), # index=882
+ ('vacuum', 'vacuum cleaner'), # index=883
+ ('vase',), # index=884
+ ('vault',), # index=885
+ ('velvet',), # index=886
+ ('vending machine',), # index=887
+ ('vestment',), # index=888
+ ('viaduct',), # index=889
+ ('violin', 'fiddle'), # index=890
+ ('volleyball',), # index=891
+ ('waffle iron',), # index=892
+ ('wall clock',), # index=893
+ ('wallet', 'billfold', 'notecase', 'pocketbook'), # index=894
+ ('wardrobe', 'closet', 'press'), # index=895
+ ('warplane', 'military plane'), # index=896
+ ('washbasin', 'handbasin', 'washbowl', 'lavabo',
+ 'wash-hand basin'), # index=897
+ ('washer', 'automatic washer', 'washing machine'), # index=898
+ ('water bottle',), # index=899
+ ('water jug',), # index=900
+ ('water tower',), # index=901
+ ('whiskey jug',), # index=902
+ ('whistle',), # index=903
+ ('wig',), # index=904
+ ('window screen',), # index=905
+ ('window shade',), # index=906
+ ('Windsor tie',), # index=907
+ ('wine bottle',), # index=908
+ ('wing',), # index=909
+ ('wok',), # index=910
+ ('wooden spoon',), # index=911
+ ('wool', 'woolen', 'woollen'), # index=912
+ ('worm fence', 'snake fence', 'snake-rail fence',
+ 'Virginia fence'), # index=913
+ ('wreck',), # index=914
+ ('yawl',), # index=915
+ ('yurt',), # index=916
+ ('web site', 'website', 'internet site', 'site'), # index=917
+ ('comic book',), # index=918
+ ('crossword puzzle', 'crossword'), # index=919
+ ('street sign',), # index=920
+ ('traffic light', 'traffic signal', 'stoplight'), # index=921
+ ('book jacket', 'dust cover', 'dust jacket', 'dust wrapper'), # index=922
+ ('menu',), # index=923
+ ('plate',), # index=924
+ ('guacamole',), # index=925
+ ('consomme',), # index=926
+ ('hot pot', 'hotpot'), # index=927
+ ('trifle',), # index=928
+ ('ice cream', 'icecream'), # index=929
+ ('ice lolly', 'lolly', 'lollipop', 'popsicle'), # index=930
+ ('French loaf',), # index=931
+ ('bagel', 'beigel'), # index=932
+ ('pretzel',), # index=933
+ ('cheeseburger',), # index=934
+ ('hotdog', 'hot dog', 'red hot'), # index=935
+ ('mashed potato',), # index=936
+ ('head cabbage',), # index=937
+ ('broccoli',), # index=938
+ ('cauliflower',), # index=939
+ ('zucchini', 'courgette'), # index=940
+ ('spaghetti squash',), # index=941
+ ('acorn squash',), # index=942
+ ('butternut squash',), # index=943
+ ('cucumber', 'cuke'), # index=944
+ ('artichoke', 'globe artichoke'), # index=945
+ ('bell pepper',), # index=946
+ ('cardoon',), # index=947
+ ('mushroom',), # index=948
+ ('Granny Smith',), # index=949
+ ('strawberry',), # index=950
+ ('orange',), # index=951
+ ('lemon',), # index=952
+ ('fig',), # index=953
+ ('pineapple', 'ananas'), # index=954
+ ('banana',), # index=955
+ ('jackfruit', 'jak', 'jack'), # index=956
+ ('custard apple',), # index=957
+ ('pomegranate',), # index=958
+ ('hay',), # index=959
+ ('carbonara',), # index=960
+ ('chocolate sauce', 'chocolate syrup'), # index=961
+ ('dough',), # index=962
+ ('meat loaf', 'meatloaf'), # index=963
+ ('pizza', 'pizza pie'), # index=964
+ ('potpie',), # index=965
+ ('burrito',), # index=966
+ ('red wine',), # index=967
+ ('espresso',), # index=968
+ ('cup',), # index=969
+ ('eggnog',), # index=970
+ ('alp',), # index=971
+ ('bubble',), # index=972
+ ('cliff', 'drop', 'drop-off'), # index=973
+ ('coral reef',), # index=974
+ ('geyser',), # index=975
+ ('lakeside', 'lakeshore'), # index=976
+ ('promontory', 'headland', 'head', 'foreland'), # index=977
+ ('sandbar', 'sand bar'), # index=978
+ ('seashore', 'coast', 'seacoast', 'sea-coast'), # index=979
+ ('valley', 'vale'), # index=980
+ ('volcano',), # index=981
+ ('ballplayer', 'baseball player'), # index=982
+ ('groom', 'bridegroom'), # index=983
+ ('scuba diver',), # index=984
+ ('rapeseed',), # index=985
+ ('daisy',), # index=986
+ ("yellow lady's slipper", 'yellow lady-slipper', 'Cypripedium calceolus',
+ 'Cypripedium parviflorum'), # index=987
+ ('corn',), # index=988
+ ('acorn',), # index=989
+ ('hip', 'rose hip', 'rosehip'), # index=990
+ ('buckeye', 'horse chestnut', 'conker'), # index=991
+ ('coral fungus',), # index=992
+ ('agaric',), # index=993
+ ('gyromitra',), # index=994
+ ('stinkhorn', 'carrion fungus'), # index=995
+ ('earthstar',), # index=996
+ ('hen-of-the-woods', 'hen of the woods', 'Polyporus frondosus',
+ 'Grifola frondosa'), # index=997
+ ('bolete',), # index=998
+ ('ear', 'spike', 'capitulum'), # index=999
+ ('toilet tissue', 'toilet paper', 'bathroom tissue'), # index=1000
+)
diff --git a/src/aiy/vision/models/object_detection.py b/src/aiy/vision/models/object_detection.py
new file mode 100644
index 00000000..4d3af7c0
--- /dev/null
+++ b/src/aiy/vision/models/object_detection.py
@@ -0,0 +1,222 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""API for Object Detection tasks."""
+import math
+
+from aiy.vision.inference import ModelDescriptor
+from aiy.vision.models import utils
+from aiy.vision.models.object_detection_anchors import ANCHORS
+
+_COMPUTE_GRAPH_NAME = 'mobilenet_ssd_256res_0.125_person_cat_dog.binaryproto'
+
+
+class Object(object):
+ """Object detection result."""
+ BACKGROUND = 0
+ PERSON = 1
+ CAT = 2
+ DOG = 3
+
+ _LABELS = {
+ BACKGROUND: 'BACKGROUND',
+ PERSON: 'PERSON',
+ CAT: 'CAT',
+ DOG: 'DOG',
+ }
+
+ def __init__(self, bounding_box, kind, score):
+ """Initialization.
+
+ Args:
+ bounding_box: a tuple of 4 ints, (x, y, width, height) order.
+ kind: int, tells what object is in the bounding box.
+ score: float, confidence score.
+ """
+ self.bounding_box = bounding_box
+ self.kind = kind
+ self.score = score
+
+ def __str__(self):
+ return 'kind=%s(%d), score=%f, bbox=%s' % (self._LABELS[self.kind],
+ self.kind, self.score,
+ str(self.bounding_box))
+
+
+def _reshape(array, height, width):
+ assert len(array) == height * width
+ return [array[i * width:(i + 1) * width] for i in range(height)]
+
+
+def _decode_and_nms_detection_result(logit_scores, box_encodings, anchors,
+ score_threshold, image_size, offset):
+ """Decodes result as bounding boxes and runs Non-Maximum Suppression.
+
+ Args:
+ logit_scores: list of scores
+ box_encodings: list of bounding boxes
+ anchors: list of anchors
+ score_threshold: float, bounding box candidates below this threshold will
+ be rejected.
+ image_size: (width, height)
+ offset: (x, y)
+ Returns:
+ A list of ObjectDetection.Result.
+ """
+
+ assert len(box_encodings) == len(anchors)
+ assert len(logit_scores) == len(anchors)
+
+ x0, y0 = offset
+ results = []
+ for logit_score, box_encoding, anchor in zip(logit_scores, box_encodings,
+ anchors):
+ scores = _logit_score_to_score(logit_score)
+ max_score_index, max_score = max(enumerate(scores), key=lambda x: x[1])
+ # Skip if max score is below threshold or max score is 'background'.
+ if max_score <= score_threshold or max_score_index == 0:
+ continue
+
+ x, y, w, h = _decode_box_encoding(box_encoding, anchor, image_size)
+ results.append(Object((x0 + x, y0 + y, w, h), max_score_index, max_score))
+
+ return _non_maximum_suppression(results)
+
+
+def _logit_score_to_score(logit_score):
+ return [1.0 / (1.0 + math.exp(-val)) for val in logit_score]
+
+
+def _decode_box_encoding(box_encoding, anchor, image_size):
+ """Decodes bounding box encoding.
+
+ Args:
+ box_encoding: a tuple of 4 floats.
+ anchor: a tuple of 4 floats.
+ image_size: a tuple of 2 ints, (width, height)
+ Returns:
+ A tuple of 4 integer, in the order of (left, upper, right, lower).
+ """
+ assert len(box_encoding) == 4
+ assert len(anchor) == 4
+ y_scale = 10.0
+ x_scale = 10.0
+ height_scale = 5.0
+ width_scale = 5.0
+
+ rel_y_translation = box_encoding[0] / y_scale
+ rel_x_translation = box_encoding[1] / x_scale
+ rel_height_dilation = box_encoding[2] / height_scale
+ rel_width_dilation = box_encoding[3] / width_scale
+
+ anchor_ymin, anchor_xmin, anchor_ymax, anchor_xmax = anchor
+ anchor_ycenter = (anchor_ymax + anchor_ymin) / 2
+ anchor_xcenter = (anchor_xmax + anchor_xmin) / 2
+ anchor_height = anchor_ymax - anchor_ymin
+ anchor_width = anchor_xmax - anchor_xmin
+
+ ycenter = anchor_ycenter + anchor_height * rel_y_translation
+ xcenter = anchor_xcenter + anchor_width * rel_x_translation
+ height = math.exp(rel_height_dilation) * anchor_height
+ width = math.exp(rel_width_dilation) * anchor_width
+
+ image_width, image_height = image_size
+ x0 = int(max(0.0, xcenter - width / 2) * image_width)
+ y0 = int(max(0.0, ycenter - height / 2) * image_height)
+ x1 = int(min(1.0, xcenter + width / 2) * image_width)
+ y1 = int(min(1.0, ycenter + height / 2) * image_height)
+ return (x0, y0, x1 - x0, y1 - y0)
+
+
+def _overlap_ratio(box1, box2):
+ """Computes overlap ratio of two bounding boxes.
+
+ Args:
+ box1: (x, y, width, height).
+ box2: (x, y, width, height).
+
+ Returns:
+ float, represents overlap ratio between given boxes.
+ """
+
+ def _area(box):
+ _, _, width, height = box
+ area = width * height
+ assert area >= 0
+ return area
+
+ def _intersection_area(box1, box2):
+ x1, y1, width1, height1 = box1
+ x2, y2, width2, height2 = box2
+ x = max(x1, x2)
+ y = max(y1, y2)
+ width = max(min(x1 + width1, x2 + width2) - x, 0)
+ height = max(min(y1 + height1, y2 + height2) - y, 0)
+ area = width * height
+ assert area >= 0
+ return area
+
+ intersection_area = _intersection_area(box1, box2)
+ union_area = _area(box1) + _area(box2) - intersection_area
+ assert union_area >= 0
+ if union_area > 0:
+ return float(intersection_area) / float(union_area)
+ return 1.0
+
+
+def _non_maximum_suppression(boxes, overlap_threshold=0.5):
+ """Runs Non Maximum Suppression.
+
+ Removes box candidate that overlaps with existing candidate who has higher
+ score.
+
+ Args:
+ boxes: list of Object
+ overlap_threshold: float
+ Returns:
+ A list of Object
+ """
+ boxes = sorted(boxes, key=lambda x: x.score, reverse=True)
+ for i in range(len(boxes)):
+ if boxes[i].score < 0.0:
+ continue
+ # Suppress any nearby bounding boxes having lower score than boxes[i]
+ for j in range(i + 1, len(boxes)):
+ if boxes[j].score < 0.0:
+ continue
+ if _overlap_ratio(boxes[i].bounding_box,
+ boxes[j].bounding_box) > overlap_threshold:
+ boxes[j].score = -1.0 # Suppress box
+
+ return [box for box in boxes if box.score >= 0.0] # Exclude suppressed boxes
+
+
+def model():
+ return ModelDescriptor(
+ name='object_detection',
+ input_shape=(1, 256, 256, 3),
+ input_normalizer=(128.0, 128.0),
+ compute_graph=utils.load_compute_graph(_COMPUTE_GRAPH_NAME))
+
+
+# TODO: check all tensor shapes
+def get_objects(result, score_threshold=0.3, offset=(0, 0)):
+ assert len(result.tensors) == 2
+ logit_scores = result.tensors['concat_1'].data
+ logit_scores = _reshape(logit_scores, len(ANCHORS), 4)
+ box_encodings = result.tensors['concat'].data
+ box_encodings = _reshape(box_encodings, len(ANCHORS), 4)
+
+ size = (result.window.width, result.window.height)
+ return _decode_and_nms_detection_result(logit_scores, box_encodings, ANCHORS,
+ score_threshold, size, offset)
diff --git a/src/aiy/vision/models/object_detection_anchors.py b/src/aiy/vision/models/object_detection_anchors.py
new file mode 100644
index 00000000..dd2b7d2c
--- /dev/null
+++ b/src/aiy/vision/models/object_detection_anchors.py
@@ -0,0 +1,1295 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Predefined anchors for object detection model."""
+
+ANCHORS = (
+ (-0.018750000745, -0.018750000745, 0.08124999702, 0.08124999702),
+ (-0.039460681379, -0.11017136276, 0.10196068138, 0.17267136276),
+ (-0.11017136276, -0.039460681379, 0.17267136276, 0.10196068138),
+ (-0.018750000745, 0.043749999255, 0.08124999702, 0.14374999702),
+ (-0.039460681379, -0.047671362758, 0.10196068138, 0.23517136276),
+ (-0.11017136276, 0.023039318621, 0.17267136276, 0.16446068883),
+ (-0.018750000745, 0.10625000298, 0.08124999702, 0.20624999702),
+ (-0.039460681379, 0.014828637242, 0.10196068138, 0.29767137766),
+ (-0.11017136276, 0.085539318621, 0.17267136276, 0.22696068883),
+ (-0.018750000745, 0.16875000298, 0.08124999702, 0.26875001192),
+ (-0.039460681379, 0.077328637242, 0.10196068138, 0.36017137766),
+ (-0.11017136276, 0.14803931117, 0.17267136276, 0.28946068883),
+ (-0.018750000745, 0.23125000298, 0.08124999702, 0.33125001192),
+ (-0.039460681379, 0.13982863724, 0.10196068138, 0.42267137766),
+ (-0.11017136276, 0.21053931117, 0.17267136276, 0.35196068883),
+ (-0.018750000745, 0.29374998808, 0.08124999702, 0.39375001192),
+ (-0.039460681379, 0.20232863724, 0.10196068138, 0.48517137766),
+ (-0.11017136276, 0.27303931117, 0.17267136276, 0.41446068883),
+ (-0.018750000745, 0.35624998808, 0.08124999702, 0.45625001192),
+ (-0.039460681379, 0.26482862234, 0.10196068138, 0.54767137766),
+ (-0.11017136276, 0.33553931117, 0.17267136276, 0.47696068883),
+ (-0.018750000745, 0.41874998808, 0.08124999702, 0.51875001192),
+ (-0.039460681379, 0.32732862234, 0.10196068138, 0.61017137766),
+ (-0.11017136276, 0.39803931117, 0.17267136276, 0.53946065903),
+ (-0.018750000745, 0.48124998808, 0.08124999702, 0.58125001192),
+ (-0.039460681379, 0.38982862234, 0.10196068138, 0.67267137766),
+ (-0.11017136276, 0.46053931117, 0.17267136276, 0.60196065903),
+ (-0.018750000745, 0.54374998808, 0.08124999702, 0.64375001192),
+ (-0.039460681379, 0.45232862234, 0.10196068138, 0.73517137766),
+ (-0.11017136276, 0.52303934097, 0.17267136276, 0.66446065903),
+ (-0.018750000745, 0.60624998808, 0.08124999702, 0.70625001192),
+ (-0.039460681379, 0.51482862234, 0.10196068138, 0.79767137766),
+ (-0.11017136276, 0.58553934097, 0.17267136276, 0.72696065903),
+ (-0.018750000745, 0.66874998808, 0.08124999702, 0.76875001192),
+ (-0.039460681379, 0.57732862234, 0.10196068138, 0.86017137766),
+ (-0.11017136276, 0.64803934097, 0.17267136276, 0.78946065903),
+ (-0.018750000745, 0.73124998808, 0.08124999702, 0.83125001192),
+ (-0.039460681379, 0.63982862234, 0.10196068138, 0.92267137766),
+ (-0.11017136276, 0.71053934097, 0.17267136276, 0.85196065903),
+ (-0.018750000745, 0.79374998808, 0.08124999702, 0.89375001192),
+ (-0.039460681379, 0.70232862234, 0.10196068138, 0.98517137766),
+ (-0.11017136276, 0.77303934097, 0.17267136276, 0.91446065903),
+ (-0.018750000745, 0.85624998808, 0.08124999702, 0.95625001192),
+ (-0.039460681379, 0.76482862234, 0.10196068138, 1.0476713181),
+ (-0.11017136276, 0.83553934097, 0.17267136276, 0.97696065903),
+ (-0.018750000745, 0.91874998808, 0.08124999702, 1.0187499523),
+ (-0.039460681379, 0.82732862234, 0.10196068138, 1.1101713181),
+ (-0.11017136276, 0.89803934097, 0.17267136276, 1.039460659),
+ (0.043749999255, -0.018750000745, 0.14374999702, 0.08124999702),
+ (0.023039318621, -0.11017136276, 0.16446068883, 0.17267136276),
+ (-0.047671362758, -0.039460681379, 0.23517136276, 0.10196068138),
+ (0.043749999255, 0.043749999255, 0.14374999702, 0.14374999702),
+ (0.023039318621, -0.047671362758, 0.16446068883, 0.23517136276),
+ (-0.047671362758, 0.023039318621, 0.23517136276, 0.16446068883),
+ (0.043749999255, 0.10625000298, 0.14374999702, 0.20624999702),
+ (0.023039318621, 0.014828637242, 0.16446068883, 0.29767137766),
+ (-0.047671362758, 0.085539318621, 0.23517136276, 0.22696068883),
+ (0.043749999255, 0.16875000298, 0.14374999702, 0.26875001192),
+ (0.023039318621, 0.077328637242, 0.16446068883, 0.36017137766),
+ (-0.047671362758, 0.14803931117, 0.23517136276, 0.28946068883),
+ (0.043749999255, 0.23125000298, 0.14374999702, 0.33125001192),
+ (0.023039318621, 0.13982863724, 0.16446068883, 0.42267137766),
+ (-0.047671362758, 0.21053931117, 0.23517136276, 0.35196068883),
+ (0.043749999255, 0.29374998808, 0.14374999702, 0.39375001192),
+ (0.023039318621, 0.20232863724, 0.16446068883, 0.48517137766),
+ (-0.047671362758, 0.27303931117, 0.23517136276, 0.41446068883),
+ (0.043749999255, 0.35624998808, 0.14374999702, 0.45625001192),
+ (0.023039318621, 0.26482862234, 0.16446068883, 0.54767137766),
+ (-0.047671362758, 0.33553931117, 0.23517136276, 0.47696068883),
+ (0.043749999255, 0.41874998808, 0.14374999702, 0.51875001192),
+ (0.023039318621, 0.32732862234, 0.16446068883, 0.61017137766),
+ (-0.047671362758, 0.39803931117, 0.23517136276, 0.53946065903),
+ (0.043749999255, 0.48124998808, 0.14374999702, 0.58125001192),
+ (0.023039318621, 0.38982862234, 0.16446068883, 0.67267137766),
+ (-0.047671362758, 0.46053931117, 0.23517136276, 0.60196065903),
+ (0.043749999255, 0.54374998808, 0.14374999702, 0.64375001192),
+ (0.023039318621, 0.45232862234, 0.16446068883, 0.73517137766),
+ (-0.047671362758, 0.52303934097, 0.23517136276, 0.66446065903),
+ (0.043749999255, 0.60624998808, 0.14374999702, 0.70625001192),
+ (0.023039318621, 0.51482862234, 0.16446068883, 0.79767137766),
+ (-0.047671362758, 0.58553934097, 0.23517136276, 0.72696065903),
+ (0.043749999255, 0.66874998808, 0.14374999702, 0.76875001192),
+ (0.023039318621, 0.57732862234, 0.16446068883, 0.86017137766),
+ (-0.047671362758, 0.64803934097, 0.23517136276, 0.78946065903),
+ (0.043749999255, 0.73124998808, 0.14374999702, 0.83125001192),
+ (0.023039318621, 0.63982862234, 0.16446068883, 0.92267137766),
+ (-0.047671362758, 0.71053934097, 0.23517136276, 0.85196065903),
+ (0.043749999255, 0.79374998808, 0.14374999702, 0.89375001192),
+ (0.023039318621, 0.70232862234, 0.16446068883, 0.98517137766),
+ (-0.047671362758, 0.77303934097, 0.23517136276, 0.91446065903),
+ (0.043749999255, 0.85624998808, 0.14374999702, 0.95625001192),
+ (0.023039318621, 0.76482862234, 0.16446068883, 1.0476713181),
+ (-0.047671362758, 0.83553934097, 0.23517136276, 0.97696065903),
+ (0.043749999255, 0.91874998808, 0.14374999702, 1.0187499523),
+ (0.023039318621, 0.82732862234, 0.16446068883, 1.1101713181),
+ (-0.047671362758, 0.89803934097, 0.23517136276, 1.039460659),
+ (0.10625000298, -0.018750000745, 0.20624999702, 0.08124999702),
+ (0.085539318621, -0.11017136276, 0.22696068883, 0.17267136276),
+ (0.014828637242, -0.039460681379, 0.29767137766, 0.10196068138),
+ (0.10625000298, 0.043749999255, 0.20624999702, 0.14374999702),
+ (0.085539318621, -0.047671362758, 0.22696068883, 0.23517136276),
+ (0.014828637242, 0.023039318621, 0.29767137766, 0.16446068883),
+ (0.10625000298, 0.10625000298, 0.20624999702, 0.20624999702),
+ (0.085539318621, 0.014828637242, 0.22696068883, 0.29767137766),
+ (0.014828637242, 0.085539318621, 0.29767137766, 0.22696068883),
+ (0.10625000298, 0.16875000298, 0.20624999702, 0.26875001192),
+ (0.085539318621, 0.077328637242, 0.22696068883, 0.36017137766),
+ (0.014828637242, 0.14803931117, 0.29767137766, 0.28946068883),
+ (0.10625000298, 0.23125000298, 0.20624999702, 0.33125001192),
+ (0.085539318621, 0.13982863724, 0.22696068883, 0.42267137766),
+ (0.014828637242, 0.21053931117, 0.29767137766, 0.35196068883),
+ (0.10625000298, 0.29374998808, 0.20624999702, 0.39375001192),
+ (0.085539318621, 0.20232863724, 0.22696068883, 0.48517137766),
+ (0.014828637242, 0.27303931117, 0.29767137766, 0.41446068883),
+ (0.10625000298, 0.35624998808, 0.20624999702, 0.45625001192),
+ (0.085539318621, 0.26482862234, 0.22696068883, 0.54767137766),
+ (0.014828637242, 0.33553931117, 0.29767137766, 0.47696068883),
+ (0.10625000298, 0.41874998808, 0.20624999702, 0.51875001192),
+ (0.085539318621, 0.32732862234, 0.22696068883, 0.61017137766),
+ (0.014828637242, 0.39803931117, 0.29767137766, 0.53946065903),
+ (0.10625000298, 0.48124998808, 0.20624999702, 0.58125001192),
+ (0.085539318621, 0.38982862234, 0.22696068883, 0.67267137766),
+ (0.014828637242, 0.46053931117, 0.29767137766, 0.60196065903),
+ (0.10625000298, 0.54374998808, 0.20624999702, 0.64375001192),
+ (0.085539318621, 0.45232862234, 0.22696068883, 0.73517137766),
+ (0.014828637242, 0.52303934097, 0.29767137766, 0.66446065903),
+ (0.10625000298, 0.60624998808, 0.20624999702, 0.70625001192),
+ (0.085539318621, 0.51482862234, 0.22696068883, 0.79767137766),
+ (0.014828637242, 0.58553934097, 0.29767137766, 0.72696065903),
+ (0.10625000298, 0.66874998808, 0.20624999702, 0.76875001192),
+ (0.085539318621, 0.57732862234, 0.22696068883, 0.86017137766),
+ (0.014828637242, 0.64803934097, 0.29767137766, 0.78946065903),
+ (0.10625000298, 0.73124998808, 0.20624999702, 0.83125001192),
+ (0.085539318621, 0.63982862234, 0.22696068883, 0.92267137766),
+ (0.014828637242, 0.71053934097, 0.29767137766, 0.85196065903),
+ (0.10625000298, 0.79374998808, 0.20624999702, 0.89375001192),
+ (0.085539318621, 0.70232862234, 0.22696068883, 0.98517137766),
+ (0.014828637242, 0.77303934097, 0.29767137766, 0.91446065903),
+ (0.10625000298, 0.85624998808, 0.20624999702, 0.95625001192),
+ (0.085539318621, 0.76482862234, 0.22696068883, 1.0476713181),
+ (0.014828637242, 0.83553934097, 0.29767137766, 0.97696065903),
+ (0.10625000298, 0.91874998808, 0.20624999702, 1.0187499523),
+ (0.085539318621, 0.82732862234, 0.22696068883, 1.1101713181),
+ (0.014828637242, 0.89803934097, 0.29767137766, 1.039460659),
+ (0.16875000298, -0.018750000745, 0.26875001192, 0.08124999702),
+ (0.14803931117, -0.11017136276, 0.28946068883, 0.17267136276),
+ (0.077328637242, -0.039460681379, 0.36017137766, 0.10196068138),
+ (0.16875000298, 0.043749999255, 0.26875001192, 0.14374999702),
+ (0.14803931117, -0.047671362758, 0.28946068883, 0.23517136276),
+ (0.077328637242, 0.023039318621, 0.36017137766, 0.16446068883),
+ (0.16875000298, 0.10625000298, 0.26875001192, 0.20624999702),
+ (0.14803931117, 0.014828637242, 0.28946068883, 0.29767137766),
+ (0.077328637242, 0.085539318621, 0.36017137766, 0.22696068883),
+ (0.16875000298, 0.16875000298, 0.26875001192, 0.26875001192),
+ (0.14803931117, 0.077328637242, 0.28946068883, 0.36017137766),
+ (0.077328637242, 0.14803931117, 0.36017137766, 0.28946068883),
+ (0.16875000298, 0.23125000298, 0.26875001192, 0.33125001192),
+ (0.14803931117, 0.13982863724, 0.28946068883, 0.42267137766),
+ (0.077328637242, 0.21053931117, 0.36017137766, 0.35196068883),
+ (0.16875000298, 0.29374998808, 0.26875001192, 0.39375001192),
+ (0.14803931117, 0.20232863724, 0.28946068883, 0.48517137766),
+ (0.077328637242, 0.27303931117, 0.36017137766, 0.41446068883),
+ (0.16875000298, 0.35624998808, 0.26875001192, 0.45625001192),
+ (0.14803931117, 0.26482862234, 0.28946068883, 0.54767137766),
+ (0.077328637242, 0.33553931117, 0.36017137766, 0.47696068883),
+ (0.16875000298, 0.41874998808, 0.26875001192, 0.51875001192),
+ (0.14803931117, 0.32732862234, 0.28946068883, 0.61017137766),
+ (0.077328637242, 0.39803931117, 0.36017137766, 0.53946065903),
+ (0.16875000298, 0.48124998808, 0.26875001192, 0.58125001192),
+ (0.14803931117, 0.38982862234, 0.28946068883, 0.67267137766),
+ (0.077328637242, 0.46053931117, 0.36017137766, 0.60196065903),
+ (0.16875000298, 0.54374998808, 0.26875001192, 0.64375001192),
+ (0.14803931117, 0.45232862234, 0.28946068883, 0.73517137766),
+ (0.077328637242, 0.52303934097, 0.36017137766, 0.66446065903),
+ (0.16875000298, 0.60624998808, 0.26875001192, 0.70625001192),
+ (0.14803931117, 0.51482862234, 0.28946068883, 0.79767137766),
+ (0.077328637242, 0.58553934097, 0.36017137766, 0.72696065903),
+ (0.16875000298, 0.66874998808, 0.26875001192, 0.76875001192),
+ (0.14803931117, 0.57732862234, 0.28946068883, 0.86017137766),
+ (0.077328637242, 0.64803934097, 0.36017137766, 0.78946065903),
+ (0.16875000298, 0.73124998808, 0.26875001192, 0.83125001192),
+ (0.14803931117, 0.63982862234, 0.28946068883, 0.92267137766),
+ (0.077328637242, 0.71053934097, 0.36017137766, 0.85196065903),
+ (0.16875000298, 0.79374998808, 0.26875001192, 0.89375001192),
+ (0.14803931117, 0.70232862234, 0.28946068883, 0.98517137766),
+ (0.077328637242, 0.77303934097, 0.36017137766, 0.91446065903),
+ (0.16875000298, 0.85624998808, 0.26875001192, 0.95625001192),
+ (0.14803931117, 0.76482862234, 0.28946068883, 1.0476713181),
+ (0.077328637242, 0.83553934097, 0.36017137766, 0.97696065903),
+ (0.16875000298, 0.91874998808, 0.26875001192, 1.0187499523),
+ (0.14803931117, 0.82732862234, 0.28946068883, 1.1101713181),
+ (0.077328637242, 0.89803934097, 0.36017137766, 1.039460659),
+ (0.23125000298, -0.018750000745, 0.33125001192, 0.08124999702),
+ (0.21053931117, -0.11017136276, 0.35196068883, 0.17267136276),
+ (0.13982863724, -0.039460681379, 0.42267137766, 0.10196068138),
+ (0.23125000298, 0.043749999255, 0.33125001192, 0.14374999702),
+ (0.21053931117, -0.047671362758, 0.35196068883, 0.23517136276),
+ (0.13982863724, 0.023039318621, 0.42267137766, 0.16446068883),
+ (0.23125000298, 0.10625000298, 0.33125001192, 0.20624999702),
+ (0.21053931117, 0.014828637242, 0.35196068883, 0.29767137766),
+ (0.13982863724, 0.085539318621, 0.42267137766, 0.22696068883),
+ (0.23125000298, 0.16875000298, 0.33125001192, 0.26875001192),
+ (0.21053931117, 0.077328637242, 0.35196068883, 0.36017137766),
+ (0.13982863724, 0.14803931117, 0.42267137766, 0.28946068883),
+ (0.23125000298, 0.23125000298, 0.33125001192, 0.33125001192),
+ (0.21053931117, 0.13982863724, 0.35196068883, 0.42267137766),
+ (0.13982863724, 0.21053931117, 0.42267137766, 0.35196068883),
+ (0.23125000298, 0.29374998808, 0.33125001192, 0.39375001192),
+ (0.21053931117, 0.20232863724, 0.35196068883, 0.48517137766),
+ (0.13982863724, 0.27303931117, 0.42267137766, 0.41446068883),
+ (0.23125000298, 0.35624998808, 0.33125001192, 0.45625001192),
+ (0.21053931117, 0.26482862234, 0.35196068883, 0.54767137766),
+ (0.13982863724, 0.33553931117, 0.42267137766, 0.47696068883),
+ (0.23125000298, 0.41874998808, 0.33125001192, 0.51875001192),
+ (0.21053931117, 0.32732862234, 0.35196068883, 0.61017137766),
+ (0.13982863724, 0.39803931117, 0.42267137766, 0.53946065903),
+ (0.23125000298, 0.48124998808, 0.33125001192, 0.58125001192),
+ (0.21053931117, 0.38982862234, 0.35196068883, 0.67267137766),
+ (0.13982863724, 0.46053931117, 0.42267137766, 0.60196065903),
+ (0.23125000298, 0.54374998808, 0.33125001192, 0.64375001192),
+ (0.21053931117, 0.45232862234, 0.35196068883, 0.73517137766),
+ (0.13982863724, 0.52303934097, 0.42267137766, 0.66446065903),
+ (0.23125000298, 0.60624998808, 0.33125001192, 0.70625001192),
+ (0.21053931117, 0.51482862234, 0.35196068883, 0.79767137766),
+ (0.13982863724, 0.58553934097, 0.42267137766, 0.72696065903),
+ (0.23125000298, 0.66874998808, 0.33125001192, 0.76875001192),
+ (0.21053931117, 0.57732862234, 0.35196068883, 0.86017137766),
+ (0.13982863724, 0.64803934097, 0.42267137766, 0.78946065903),
+ (0.23125000298, 0.73124998808, 0.33125001192, 0.83125001192),
+ (0.21053931117, 0.63982862234, 0.35196068883, 0.92267137766),
+ (0.13982863724, 0.71053934097, 0.42267137766, 0.85196065903),
+ (0.23125000298, 0.79374998808, 0.33125001192, 0.89375001192),
+ (0.21053931117, 0.70232862234, 0.35196068883, 0.98517137766),
+ (0.13982863724, 0.77303934097, 0.42267137766, 0.91446065903),
+ (0.23125000298, 0.85624998808, 0.33125001192, 0.95625001192),
+ (0.21053931117, 0.76482862234, 0.35196068883, 1.0476713181),
+ (0.13982863724, 0.83553934097, 0.42267137766, 0.97696065903),
+ (0.23125000298, 0.91874998808, 0.33125001192, 1.0187499523),
+ (0.21053931117, 0.82732862234, 0.35196068883, 1.1101713181),
+ (0.13982863724, 0.89803934097, 0.42267137766, 1.039460659),
+ (0.29374998808, -0.018750000745, 0.39375001192, 0.08124999702),
+ (0.27303931117, -0.11017136276, 0.41446068883, 0.17267136276),
+ (0.20232863724, -0.039460681379, 0.48517137766, 0.10196068138),
+ (0.29374998808, 0.043749999255, 0.39375001192, 0.14374999702),
+ (0.27303931117, -0.047671362758, 0.41446068883, 0.23517136276),
+ (0.20232863724, 0.023039318621, 0.48517137766, 0.16446068883),
+ (0.29374998808, 0.10625000298, 0.39375001192, 0.20624999702),
+ (0.27303931117, 0.014828637242, 0.41446068883, 0.29767137766),
+ (0.20232863724, 0.085539318621, 0.48517137766, 0.22696068883),
+ (0.29374998808, 0.16875000298, 0.39375001192, 0.26875001192),
+ (0.27303931117, 0.077328637242, 0.41446068883, 0.36017137766),
+ (0.20232863724, 0.14803931117, 0.48517137766, 0.28946068883),
+ (0.29374998808, 0.23125000298, 0.39375001192, 0.33125001192),
+ (0.27303931117, 0.13982863724, 0.41446068883, 0.42267137766),
+ (0.20232863724, 0.21053931117, 0.48517137766, 0.35196068883),
+ (0.29374998808, 0.29374998808, 0.39375001192, 0.39375001192),
+ (0.27303931117, 0.20232863724, 0.41446068883, 0.48517137766),
+ (0.20232863724, 0.27303931117, 0.48517137766, 0.41446068883),
+ (0.29374998808, 0.35624998808, 0.39375001192, 0.45625001192),
+ (0.27303931117, 0.26482862234, 0.41446068883, 0.54767137766),
+ (0.20232863724, 0.33553931117, 0.48517137766, 0.47696068883),
+ (0.29374998808, 0.41874998808, 0.39375001192, 0.51875001192),
+ (0.27303931117, 0.32732862234, 0.41446068883, 0.61017137766),
+ (0.20232863724, 0.39803931117, 0.48517137766, 0.53946065903),
+ (0.29374998808, 0.48124998808, 0.39375001192, 0.58125001192),
+ (0.27303931117, 0.38982862234, 0.41446068883, 0.67267137766),
+ (0.20232863724, 0.46053931117, 0.48517137766, 0.60196065903),
+ (0.29374998808, 0.54374998808, 0.39375001192, 0.64375001192),
+ (0.27303931117, 0.45232862234, 0.41446068883, 0.73517137766),
+ (0.20232863724, 0.52303934097, 0.48517137766, 0.66446065903),
+ (0.29374998808, 0.60624998808, 0.39375001192, 0.70625001192),
+ (0.27303931117, 0.51482862234, 0.41446068883, 0.79767137766),
+ (0.20232863724, 0.58553934097, 0.48517137766, 0.72696065903),
+ (0.29374998808, 0.66874998808, 0.39375001192, 0.76875001192),
+ (0.27303931117, 0.57732862234, 0.41446068883, 0.86017137766),
+ (0.20232863724, 0.64803934097, 0.48517137766, 0.78946065903),
+ (0.29374998808, 0.73124998808, 0.39375001192, 0.83125001192),
+ (0.27303931117, 0.63982862234, 0.41446068883, 0.92267137766),
+ (0.20232863724, 0.71053934097, 0.48517137766, 0.85196065903),
+ (0.29374998808, 0.79374998808, 0.39375001192, 0.89375001192),
+ (0.27303931117, 0.70232862234, 0.41446068883, 0.98517137766),
+ (0.20232863724, 0.77303934097, 0.48517137766, 0.91446065903),
+ (0.29374998808, 0.85624998808, 0.39375001192, 0.95625001192),
+ (0.27303931117, 0.76482862234, 0.41446068883, 1.0476713181),
+ (0.20232863724, 0.83553934097, 0.48517137766, 0.97696065903),
+ (0.29374998808, 0.91874998808, 0.39375001192, 1.0187499523),
+ (0.27303931117, 0.82732862234, 0.41446068883, 1.1101713181),
+ (0.20232863724, 0.89803934097, 0.48517137766, 1.039460659),
+ (0.35624998808, -0.018750000745, 0.45625001192, 0.08124999702),
+ (0.33553931117, -0.11017136276, 0.47696068883, 0.17267136276),
+ (0.26482862234, -0.039460681379, 0.54767137766, 0.10196068138),
+ (0.35624998808, 0.043749999255, 0.45625001192, 0.14374999702),
+ (0.33553931117, -0.047671362758, 0.47696068883, 0.23517136276),
+ (0.26482862234, 0.023039318621, 0.54767137766, 0.16446068883),
+ (0.35624998808, 0.10625000298, 0.45625001192, 0.20624999702),
+ (0.33553931117, 0.014828637242, 0.47696068883, 0.29767137766),
+ (0.26482862234, 0.085539318621, 0.54767137766, 0.22696068883),
+ (0.35624998808, 0.16875000298, 0.45625001192, 0.26875001192),
+ (0.33553931117, 0.077328637242, 0.47696068883, 0.36017137766),
+ (0.26482862234, 0.14803931117, 0.54767137766, 0.28946068883),
+ (0.35624998808, 0.23125000298, 0.45625001192, 0.33125001192),
+ (0.33553931117, 0.13982863724, 0.47696068883, 0.42267137766),
+ (0.26482862234, 0.21053931117, 0.54767137766, 0.35196068883),
+ (0.35624998808, 0.29374998808, 0.45625001192, 0.39375001192),
+ (0.33553931117, 0.20232863724, 0.47696068883, 0.48517137766),
+ (0.26482862234, 0.27303931117, 0.54767137766, 0.41446068883),
+ (0.35624998808, 0.35624998808, 0.45625001192, 0.45625001192),
+ (0.33553931117, 0.26482862234, 0.47696068883, 0.54767137766),
+ (0.26482862234, 0.33553931117, 0.54767137766, 0.47696068883),
+ (0.35624998808, 0.41874998808, 0.45625001192, 0.51875001192),
+ (0.33553931117, 0.32732862234, 0.47696068883, 0.61017137766),
+ (0.26482862234, 0.39803931117, 0.54767137766, 0.53946065903),
+ (0.35624998808, 0.48124998808, 0.45625001192, 0.58125001192),
+ (0.33553931117, 0.38982862234, 0.47696068883, 0.67267137766),
+ (0.26482862234, 0.46053931117, 0.54767137766, 0.60196065903),
+ (0.35624998808, 0.54374998808, 0.45625001192, 0.64375001192),
+ (0.33553931117, 0.45232862234, 0.47696068883, 0.73517137766),
+ (0.26482862234, 0.52303934097, 0.54767137766, 0.66446065903),
+ (0.35624998808, 0.60624998808, 0.45625001192, 0.70625001192),
+ (0.33553931117, 0.51482862234, 0.47696068883, 0.79767137766),
+ (0.26482862234, 0.58553934097, 0.54767137766, 0.72696065903),
+ (0.35624998808, 0.66874998808, 0.45625001192, 0.76875001192),
+ (0.33553931117, 0.57732862234, 0.47696068883, 0.86017137766),
+ (0.26482862234, 0.64803934097, 0.54767137766, 0.78946065903),
+ (0.35624998808, 0.73124998808, 0.45625001192, 0.83125001192),
+ (0.33553931117, 0.63982862234, 0.47696068883, 0.92267137766),
+ (0.26482862234, 0.71053934097, 0.54767137766, 0.85196065903),
+ (0.35624998808, 0.79374998808, 0.45625001192, 0.89375001192),
+ (0.33553931117, 0.70232862234, 0.47696068883, 0.98517137766),
+ (0.26482862234, 0.77303934097, 0.54767137766, 0.91446065903),
+ (0.35624998808, 0.85624998808, 0.45625001192, 0.95625001192),
+ (0.33553931117, 0.76482862234, 0.47696068883, 1.0476713181),
+ (0.26482862234, 0.83553934097, 0.54767137766, 0.97696065903),
+ (0.35624998808, 0.91874998808, 0.45625001192, 1.0187499523),
+ (0.33553931117, 0.82732862234, 0.47696068883, 1.1101713181),
+ (0.26482862234, 0.89803934097, 0.54767137766, 1.039460659),
+ (0.41874998808, -0.018750000745, 0.51875001192, 0.08124999702),
+ (0.39803931117, -0.11017136276, 0.53946065903, 0.17267136276),
+ (0.32732862234, -0.039460681379, 0.61017137766, 0.10196068138),
+ (0.41874998808, 0.043749999255, 0.51875001192, 0.14374999702),
+ (0.39803931117, -0.047671362758, 0.53946065903, 0.23517136276),
+ (0.32732862234, 0.023039318621, 0.61017137766, 0.16446068883),
+ (0.41874998808, 0.10625000298, 0.51875001192, 0.20624999702),
+ (0.39803931117, 0.014828637242, 0.53946065903, 0.29767137766),
+ (0.32732862234, 0.085539318621, 0.61017137766, 0.22696068883),
+ (0.41874998808, 0.16875000298, 0.51875001192, 0.26875001192),
+ (0.39803931117, 0.077328637242, 0.53946065903, 0.36017137766),
+ (0.32732862234, 0.14803931117, 0.61017137766, 0.28946068883),
+ (0.41874998808, 0.23125000298, 0.51875001192, 0.33125001192),
+ (0.39803931117, 0.13982863724, 0.53946065903, 0.42267137766),
+ (0.32732862234, 0.21053931117, 0.61017137766, 0.35196068883),
+ (0.41874998808, 0.29374998808, 0.51875001192, 0.39375001192),
+ (0.39803931117, 0.20232863724, 0.53946065903, 0.48517137766),
+ (0.32732862234, 0.27303931117, 0.61017137766, 0.41446068883),
+ (0.41874998808, 0.35624998808, 0.51875001192, 0.45625001192),
+ (0.39803931117, 0.26482862234, 0.53946065903, 0.54767137766),
+ (0.32732862234, 0.33553931117, 0.61017137766, 0.47696068883),
+ (0.41874998808, 0.41874998808, 0.51875001192, 0.51875001192),
+ (0.39803931117, 0.32732862234, 0.53946065903, 0.61017137766),
+ (0.32732862234, 0.39803931117, 0.61017137766, 0.53946065903),
+ (0.41874998808, 0.48124998808, 0.51875001192, 0.58125001192),
+ (0.39803931117, 0.38982862234, 0.53946065903, 0.67267137766),
+ (0.32732862234, 0.46053931117, 0.61017137766, 0.60196065903),
+ (0.41874998808, 0.54374998808, 0.51875001192, 0.64375001192),
+ (0.39803931117, 0.45232862234, 0.53946065903, 0.73517137766),
+ (0.32732862234, 0.52303934097, 0.61017137766, 0.66446065903),
+ (0.41874998808, 0.60624998808, 0.51875001192, 0.70625001192),
+ (0.39803931117, 0.51482862234, 0.53946065903, 0.79767137766),
+ (0.32732862234, 0.58553934097, 0.61017137766, 0.72696065903),
+ (0.41874998808, 0.66874998808, 0.51875001192, 0.76875001192),
+ (0.39803931117, 0.57732862234, 0.53946065903, 0.86017137766),
+ (0.32732862234, 0.64803934097, 0.61017137766, 0.78946065903),
+ (0.41874998808, 0.73124998808, 0.51875001192, 0.83125001192),
+ (0.39803931117, 0.63982862234, 0.53946065903, 0.92267137766),
+ (0.32732862234, 0.71053934097, 0.61017137766, 0.85196065903),
+ (0.41874998808, 0.79374998808, 0.51875001192, 0.89375001192),
+ (0.39803931117, 0.70232862234, 0.53946065903, 0.98517137766),
+ (0.32732862234, 0.77303934097, 0.61017137766, 0.91446065903),
+ (0.41874998808, 0.85624998808, 0.51875001192, 0.95625001192),
+ (0.39803931117, 0.76482862234, 0.53946065903, 1.0476713181),
+ (0.32732862234, 0.83553934097, 0.61017137766, 0.97696065903),
+ (0.41874998808, 0.91874998808, 0.51875001192, 1.0187499523),
+ (0.39803931117, 0.82732862234, 0.53946065903, 1.1101713181),
+ (0.32732862234, 0.89803934097, 0.61017137766, 1.039460659),
+ (0.48124998808, -0.018750000745, 0.58125001192, 0.08124999702),
+ (0.46053931117, -0.11017136276, 0.60196065903, 0.17267136276),
+ (0.38982862234, -0.039460681379, 0.67267137766, 0.10196068138),
+ (0.48124998808, 0.043749999255, 0.58125001192, 0.14374999702),
+ (0.46053931117, -0.047671362758, 0.60196065903, 0.23517136276),
+ (0.38982862234, 0.023039318621, 0.67267137766, 0.16446068883),
+ (0.48124998808, 0.10625000298, 0.58125001192, 0.20624999702),
+ (0.46053931117, 0.014828637242, 0.60196065903, 0.29767137766),
+ (0.38982862234, 0.085539318621, 0.67267137766, 0.22696068883),
+ (0.48124998808, 0.16875000298, 0.58125001192, 0.26875001192),
+ (0.46053931117, 0.077328637242, 0.60196065903, 0.36017137766),
+ (0.38982862234, 0.14803931117, 0.67267137766, 0.28946068883),
+ (0.48124998808, 0.23125000298, 0.58125001192, 0.33125001192),
+ (0.46053931117, 0.13982863724, 0.60196065903, 0.42267137766),
+ (0.38982862234, 0.21053931117, 0.67267137766, 0.35196068883),
+ (0.48124998808, 0.29374998808, 0.58125001192, 0.39375001192),
+ (0.46053931117, 0.20232863724, 0.60196065903, 0.48517137766),
+ (0.38982862234, 0.27303931117, 0.67267137766, 0.41446068883),
+ (0.48124998808, 0.35624998808, 0.58125001192, 0.45625001192),
+ (0.46053931117, 0.26482862234, 0.60196065903, 0.54767137766),
+ (0.38982862234, 0.33553931117, 0.67267137766, 0.47696068883),
+ (0.48124998808, 0.41874998808, 0.58125001192, 0.51875001192),
+ (0.46053931117, 0.32732862234, 0.60196065903, 0.61017137766),
+ (0.38982862234, 0.39803931117, 0.67267137766, 0.53946065903),
+ (0.48124998808, 0.48124998808, 0.58125001192, 0.58125001192),
+ (0.46053931117, 0.38982862234, 0.60196065903, 0.67267137766),
+ (0.38982862234, 0.46053931117, 0.67267137766, 0.60196065903),
+ (0.48124998808, 0.54374998808, 0.58125001192, 0.64375001192),
+ (0.46053931117, 0.45232862234, 0.60196065903, 0.73517137766),
+ (0.38982862234, 0.52303934097, 0.67267137766, 0.66446065903),
+ (0.48124998808, 0.60624998808, 0.58125001192, 0.70625001192),
+ (0.46053931117, 0.51482862234, 0.60196065903, 0.79767137766),
+ (0.38982862234, 0.58553934097, 0.67267137766, 0.72696065903),
+ (0.48124998808, 0.66874998808, 0.58125001192, 0.76875001192),
+ (0.46053931117, 0.57732862234, 0.60196065903, 0.86017137766),
+ (0.38982862234, 0.64803934097, 0.67267137766, 0.78946065903),
+ (0.48124998808, 0.73124998808, 0.58125001192, 0.83125001192),
+ (0.46053931117, 0.63982862234, 0.60196065903, 0.92267137766),
+ (0.38982862234, 0.71053934097, 0.67267137766, 0.85196065903),
+ (0.48124998808, 0.79374998808, 0.58125001192, 0.89375001192),
+ (0.46053931117, 0.70232862234, 0.60196065903, 0.98517137766),
+ (0.38982862234, 0.77303934097, 0.67267137766, 0.91446065903),
+ (0.48124998808, 0.85624998808, 0.58125001192, 0.95625001192),
+ (0.46053931117, 0.76482862234, 0.60196065903, 1.0476713181),
+ (0.38982862234, 0.83553934097, 0.67267137766, 0.97696065903),
+ (0.48124998808, 0.91874998808, 0.58125001192, 1.0187499523),
+ (0.46053931117, 0.82732862234, 0.60196065903, 1.1101713181),
+ (0.38982862234, 0.89803934097, 0.67267137766, 1.039460659),
+ (0.54374998808, -0.018750000745, 0.64375001192, 0.08124999702),
+ (0.52303934097, -0.11017136276, 0.66446065903, 0.17267136276),
+ (0.45232862234, -0.039460681379, 0.73517137766, 0.10196068138),
+ (0.54374998808, 0.043749999255, 0.64375001192, 0.14374999702),
+ (0.52303934097, -0.047671362758, 0.66446065903, 0.23517136276),
+ (0.45232862234, 0.023039318621, 0.73517137766, 0.16446068883),
+ (0.54374998808, 0.10625000298, 0.64375001192, 0.20624999702),
+ (0.52303934097, 0.014828637242, 0.66446065903, 0.29767137766),
+ (0.45232862234, 0.085539318621, 0.73517137766, 0.22696068883),
+ (0.54374998808, 0.16875000298, 0.64375001192, 0.26875001192),
+ (0.52303934097, 0.077328637242, 0.66446065903, 0.36017137766),
+ (0.45232862234, 0.14803931117, 0.73517137766, 0.28946068883),
+ (0.54374998808, 0.23125000298, 0.64375001192, 0.33125001192),
+ (0.52303934097, 0.13982863724, 0.66446065903, 0.42267137766),
+ (0.45232862234, 0.21053931117, 0.73517137766, 0.35196068883),
+ (0.54374998808, 0.29374998808, 0.64375001192, 0.39375001192),
+ (0.52303934097, 0.20232863724, 0.66446065903, 0.48517137766),
+ (0.45232862234, 0.27303931117, 0.73517137766, 0.41446068883),
+ (0.54374998808, 0.35624998808, 0.64375001192, 0.45625001192),
+ (0.52303934097, 0.26482862234, 0.66446065903, 0.54767137766),
+ (0.45232862234, 0.33553931117, 0.73517137766, 0.47696068883),
+ (0.54374998808, 0.41874998808, 0.64375001192, 0.51875001192),
+ (0.52303934097, 0.32732862234, 0.66446065903, 0.61017137766),
+ (0.45232862234, 0.39803931117, 0.73517137766, 0.53946065903),
+ (0.54374998808, 0.48124998808, 0.64375001192, 0.58125001192),
+ (0.52303934097, 0.38982862234, 0.66446065903, 0.67267137766),
+ (0.45232862234, 0.46053931117, 0.73517137766, 0.60196065903),
+ (0.54374998808, 0.54374998808, 0.64375001192, 0.64375001192),
+ (0.52303934097, 0.45232862234, 0.66446065903, 0.73517137766),
+ (0.45232862234, 0.52303934097, 0.73517137766, 0.66446065903),
+ (0.54374998808, 0.60624998808, 0.64375001192, 0.70625001192),
+ (0.52303934097, 0.51482862234, 0.66446065903, 0.79767137766),
+ (0.45232862234, 0.58553934097, 0.73517137766, 0.72696065903),
+ (0.54374998808, 0.66874998808, 0.64375001192, 0.76875001192),
+ (0.52303934097, 0.57732862234, 0.66446065903, 0.86017137766),
+ (0.45232862234, 0.64803934097, 0.73517137766, 0.78946065903),
+ (0.54374998808, 0.73124998808, 0.64375001192, 0.83125001192),
+ (0.52303934097, 0.63982862234, 0.66446065903, 0.92267137766),
+ (0.45232862234, 0.71053934097, 0.73517137766, 0.85196065903),
+ (0.54374998808, 0.79374998808, 0.64375001192, 0.89375001192),
+ (0.52303934097, 0.70232862234, 0.66446065903, 0.98517137766),
+ (0.45232862234, 0.77303934097, 0.73517137766, 0.91446065903),
+ (0.54374998808, 0.85624998808, 0.64375001192, 0.95625001192),
+ (0.52303934097, 0.76482862234, 0.66446065903, 1.0476713181),
+ (0.45232862234, 0.83553934097, 0.73517137766, 0.97696065903),
+ (0.54374998808, 0.91874998808, 0.64375001192, 1.0187499523),
+ (0.52303934097, 0.82732862234, 0.66446065903, 1.1101713181),
+ (0.45232862234, 0.89803934097, 0.73517137766, 1.039460659),
+ (0.60624998808, -0.018750000745, 0.70625001192, 0.08124999702),
+ (0.58553934097, -0.11017136276, 0.72696065903, 0.17267136276),
+ (0.51482862234, -0.039460681379, 0.79767137766, 0.10196068138),
+ (0.60624998808, 0.043749999255, 0.70625001192, 0.14374999702),
+ (0.58553934097, -0.047671362758, 0.72696065903, 0.23517136276),
+ (0.51482862234, 0.023039318621, 0.79767137766, 0.16446068883),
+ (0.60624998808, 0.10625000298, 0.70625001192, 0.20624999702),
+ (0.58553934097, 0.014828637242, 0.72696065903, 0.29767137766),
+ (0.51482862234, 0.085539318621, 0.79767137766, 0.22696068883),
+ (0.60624998808, 0.16875000298, 0.70625001192, 0.26875001192),
+ (0.58553934097, 0.077328637242, 0.72696065903, 0.36017137766),
+ (0.51482862234, 0.14803931117, 0.79767137766, 0.28946068883),
+ (0.60624998808, 0.23125000298, 0.70625001192, 0.33125001192),
+ (0.58553934097, 0.13982863724, 0.72696065903, 0.42267137766),
+ (0.51482862234, 0.21053931117, 0.79767137766, 0.35196068883),
+ (0.60624998808, 0.29374998808, 0.70625001192, 0.39375001192),
+ (0.58553934097, 0.20232863724, 0.72696065903, 0.48517137766),
+ (0.51482862234, 0.27303931117, 0.79767137766, 0.41446068883),
+ (0.60624998808, 0.35624998808, 0.70625001192, 0.45625001192),
+ (0.58553934097, 0.26482862234, 0.72696065903, 0.54767137766),
+ (0.51482862234, 0.33553931117, 0.79767137766, 0.47696068883),
+ (0.60624998808, 0.41874998808, 0.70625001192, 0.51875001192),
+ (0.58553934097, 0.32732862234, 0.72696065903, 0.61017137766),
+ (0.51482862234, 0.39803931117, 0.79767137766, 0.53946065903),
+ (0.60624998808, 0.48124998808, 0.70625001192, 0.58125001192),
+ (0.58553934097, 0.38982862234, 0.72696065903, 0.67267137766),
+ (0.51482862234, 0.46053931117, 0.79767137766, 0.60196065903),
+ (0.60624998808, 0.54374998808, 0.70625001192, 0.64375001192),
+ (0.58553934097, 0.45232862234, 0.72696065903, 0.73517137766),
+ (0.51482862234, 0.52303934097, 0.79767137766, 0.66446065903),
+ (0.60624998808, 0.60624998808, 0.70625001192, 0.70625001192),
+ (0.58553934097, 0.51482862234, 0.72696065903, 0.79767137766),
+ (0.51482862234, 0.58553934097, 0.79767137766, 0.72696065903),
+ (0.60624998808, 0.66874998808, 0.70625001192, 0.76875001192),
+ (0.58553934097, 0.57732862234, 0.72696065903, 0.86017137766),
+ (0.51482862234, 0.64803934097, 0.79767137766, 0.78946065903),
+ (0.60624998808, 0.73124998808, 0.70625001192, 0.83125001192),
+ (0.58553934097, 0.63982862234, 0.72696065903, 0.92267137766),
+ (0.51482862234, 0.71053934097, 0.79767137766, 0.85196065903),
+ (0.60624998808, 0.79374998808, 0.70625001192, 0.89375001192),
+ (0.58553934097, 0.70232862234, 0.72696065903, 0.98517137766),
+ (0.51482862234, 0.77303934097, 0.79767137766, 0.91446065903),
+ (0.60624998808, 0.85624998808, 0.70625001192, 0.95625001192),
+ (0.58553934097, 0.76482862234, 0.72696065903, 1.0476713181),
+ (0.51482862234, 0.83553934097, 0.79767137766, 0.97696065903),
+ (0.60624998808, 0.91874998808, 0.70625001192, 1.0187499523),
+ (0.58553934097, 0.82732862234, 0.72696065903, 1.1101713181),
+ (0.51482862234, 0.89803934097, 0.79767137766, 1.039460659),
+ (0.66874998808, -0.018750000745, 0.76875001192, 0.08124999702),
+ (0.64803934097, -0.11017136276, 0.78946065903, 0.17267136276),
+ (0.57732862234, -0.039460681379, 0.86017137766, 0.10196068138),
+ (0.66874998808, 0.043749999255, 0.76875001192, 0.14374999702),
+ (0.64803934097, -0.047671362758, 0.78946065903, 0.23517136276),
+ (0.57732862234, 0.023039318621, 0.86017137766, 0.16446068883),
+ (0.66874998808, 0.10625000298, 0.76875001192, 0.20624999702),
+ (0.64803934097, 0.014828637242, 0.78946065903, 0.29767137766),
+ (0.57732862234, 0.085539318621, 0.86017137766, 0.22696068883),
+ (0.66874998808, 0.16875000298, 0.76875001192, 0.26875001192),
+ (0.64803934097, 0.077328637242, 0.78946065903, 0.36017137766),
+ (0.57732862234, 0.14803931117, 0.86017137766, 0.28946068883),
+ (0.66874998808, 0.23125000298, 0.76875001192, 0.33125001192),
+ (0.64803934097, 0.13982863724, 0.78946065903, 0.42267137766),
+ (0.57732862234, 0.21053931117, 0.86017137766, 0.35196068883),
+ (0.66874998808, 0.29374998808, 0.76875001192, 0.39375001192),
+ (0.64803934097, 0.20232863724, 0.78946065903, 0.48517137766),
+ (0.57732862234, 0.27303931117, 0.86017137766, 0.41446068883),
+ (0.66874998808, 0.35624998808, 0.76875001192, 0.45625001192),
+ (0.64803934097, 0.26482862234, 0.78946065903, 0.54767137766),
+ (0.57732862234, 0.33553931117, 0.86017137766, 0.47696068883),
+ (0.66874998808, 0.41874998808, 0.76875001192, 0.51875001192),
+ (0.64803934097, 0.32732862234, 0.78946065903, 0.61017137766),
+ (0.57732862234, 0.39803931117, 0.86017137766, 0.53946065903),
+ (0.66874998808, 0.48124998808, 0.76875001192, 0.58125001192),
+ (0.64803934097, 0.38982862234, 0.78946065903, 0.67267137766),
+ (0.57732862234, 0.46053931117, 0.86017137766, 0.60196065903),
+ (0.66874998808, 0.54374998808, 0.76875001192, 0.64375001192),
+ (0.64803934097, 0.45232862234, 0.78946065903, 0.73517137766),
+ (0.57732862234, 0.52303934097, 0.86017137766, 0.66446065903),
+ (0.66874998808, 0.60624998808, 0.76875001192, 0.70625001192),
+ (0.64803934097, 0.51482862234, 0.78946065903, 0.79767137766),
+ (0.57732862234, 0.58553934097, 0.86017137766, 0.72696065903),
+ (0.66874998808, 0.66874998808, 0.76875001192, 0.76875001192),
+ (0.64803934097, 0.57732862234, 0.78946065903, 0.86017137766),
+ (0.57732862234, 0.64803934097, 0.86017137766, 0.78946065903),
+ (0.66874998808, 0.73124998808, 0.76875001192, 0.83125001192),
+ (0.64803934097, 0.63982862234, 0.78946065903, 0.92267137766),
+ (0.57732862234, 0.71053934097, 0.86017137766, 0.85196065903),
+ (0.66874998808, 0.79374998808, 0.76875001192, 0.89375001192),
+ (0.64803934097, 0.70232862234, 0.78946065903, 0.98517137766),
+ (0.57732862234, 0.77303934097, 0.86017137766, 0.91446065903),
+ (0.66874998808, 0.85624998808, 0.76875001192, 0.95625001192),
+ (0.64803934097, 0.76482862234, 0.78946065903, 1.0476713181),
+ (0.57732862234, 0.83553934097, 0.86017137766, 0.97696065903),
+ (0.66874998808, 0.91874998808, 0.76875001192, 1.0187499523),
+ (0.64803934097, 0.82732862234, 0.78946065903, 1.1101713181),
+ (0.57732862234, 0.89803934097, 0.86017137766, 1.039460659),
+ (0.73124998808, -0.018750000745, 0.83125001192, 0.08124999702),
+ (0.71053934097, -0.11017136276, 0.85196065903, 0.17267136276),
+ (0.63982862234, -0.039460681379, 0.92267137766, 0.10196068138),
+ (0.73124998808, 0.043749999255, 0.83125001192, 0.14374999702),
+ (0.71053934097, -0.047671362758, 0.85196065903, 0.23517136276),
+ (0.63982862234, 0.023039318621, 0.92267137766, 0.16446068883),
+ (0.73124998808, 0.10625000298, 0.83125001192, 0.20624999702),
+ (0.71053934097, 0.014828637242, 0.85196065903, 0.29767137766),
+ (0.63982862234, 0.085539318621, 0.92267137766, 0.22696068883),
+ (0.73124998808, 0.16875000298, 0.83125001192, 0.26875001192),
+ (0.71053934097, 0.077328637242, 0.85196065903, 0.36017137766),
+ (0.63982862234, 0.14803931117, 0.92267137766, 0.28946068883),
+ (0.73124998808, 0.23125000298, 0.83125001192, 0.33125001192),
+ (0.71053934097, 0.13982863724, 0.85196065903, 0.42267137766),
+ (0.63982862234, 0.21053931117, 0.92267137766, 0.35196068883),
+ (0.73124998808, 0.29374998808, 0.83125001192, 0.39375001192),
+ (0.71053934097, 0.20232863724, 0.85196065903, 0.48517137766),
+ (0.63982862234, 0.27303931117, 0.92267137766, 0.41446068883),
+ (0.73124998808, 0.35624998808, 0.83125001192, 0.45625001192),
+ (0.71053934097, 0.26482862234, 0.85196065903, 0.54767137766),
+ (0.63982862234, 0.33553931117, 0.92267137766, 0.47696068883),
+ (0.73124998808, 0.41874998808, 0.83125001192, 0.51875001192),
+ (0.71053934097, 0.32732862234, 0.85196065903, 0.61017137766),
+ (0.63982862234, 0.39803931117, 0.92267137766, 0.53946065903),
+ (0.73124998808, 0.48124998808, 0.83125001192, 0.58125001192),
+ (0.71053934097, 0.38982862234, 0.85196065903, 0.67267137766),
+ (0.63982862234, 0.46053931117, 0.92267137766, 0.60196065903),
+ (0.73124998808, 0.54374998808, 0.83125001192, 0.64375001192),
+ (0.71053934097, 0.45232862234, 0.85196065903, 0.73517137766),
+ (0.63982862234, 0.52303934097, 0.92267137766, 0.66446065903),
+ (0.73124998808, 0.60624998808, 0.83125001192, 0.70625001192),
+ (0.71053934097, 0.51482862234, 0.85196065903, 0.79767137766),
+ (0.63982862234, 0.58553934097, 0.92267137766, 0.72696065903),
+ (0.73124998808, 0.66874998808, 0.83125001192, 0.76875001192),
+ (0.71053934097, 0.57732862234, 0.85196065903, 0.86017137766),
+ (0.63982862234, 0.64803934097, 0.92267137766, 0.78946065903),
+ (0.73124998808, 0.73124998808, 0.83125001192, 0.83125001192),
+ (0.71053934097, 0.63982862234, 0.85196065903, 0.92267137766),
+ (0.63982862234, 0.71053934097, 0.92267137766, 0.85196065903),
+ (0.73124998808, 0.79374998808, 0.83125001192, 0.89375001192),
+ (0.71053934097, 0.70232862234, 0.85196065903, 0.98517137766),
+ (0.63982862234, 0.77303934097, 0.92267137766, 0.91446065903),
+ (0.73124998808, 0.85624998808, 0.83125001192, 0.95625001192),
+ (0.71053934097, 0.76482862234, 0.85196065903, 1.0476713181),
+ (0.63982862234, 0.83553934097, 0.92267137766, 0.97696065903),
+ (0.73124998808, 0.91874998808, 0.83125001192, 1.0187499523),
+ (0.71053934097, 0.82732862234, 0.85196065903, 1.1101713181),
+ (0.63982862234, 0.89803934097, 0.92267137766, 1.039460659),
+ (0.79374998808, -0.018750000745, 0.89375001192, 0.08124999702),
+ (0.77303934097, -0.11017136276, 0.91446065903, 0.17267136276),
+ (0.70232862234, -0.039460681379, 0.98517137766, 0.10196068138),
+ (0.79374998808, 0.043749999255, 0.89375001192, 0.14374999702),
+ (0.77303934097, -0.047671362758, 0.91446065903, 0.23517136276),
+ (0.70232862234, 0.023039318621, 0.98517137766, 0.16446068883),
+ (0.79374998808, 0.10625000298, 0.89375001192, 0.20624999702),
+ (0.77303934097, 0.014828637242, 0.91446065903, 0.29767137766),
+ (0.70232862234, 0.085539318621, 0.98517137766, 0.22696068883),
+ (0.79374998808, 0.16875000298, 0.89375001192, 0.26875001192),
+ (0.77303934097, 0.077328637242, 0.91446065903, 0.36017137766),
+ (0.70232862234, 0.14803931117, 0.98517137766, 0.28946068883),
+ (0.79374998808, 0.23125000298, 0.89375001192, 0.33125001192),
+ (0.77303934097, 0.13982863724, 0.91446065903, 0.42267137766),
+ (0.70232862234, 0.21053931117, 0.98517137766, 0.35196068883),
+ (0.79374998808, 0.29374998808, 0.89375001192, 0.39375001192),
+ (0.77303934097, 0.20232863724, 0.91446065903, 0.48517137766),
+ (0.70232862234, 0.27303931117, 0.98517137766, 0.41446068883),
+ (0.79374998808, 0.35624998808, 0.89375001192, 0.45625001192),
+ (0.77303934097, 0.26482862234, 0.91446065903, 0.54767137766),
+ (0.70232862234, 0.33553931117, 0.98517137766, 0.47696068883),
+ (0.79374998808, 0.41874998808, 0.89375001192, 0.51875001192),
+ (0.77303934097, 0.32732862234, 0.91446065903, 0.61017137766),
+ (0.70232862234, 0.39803931117, 0.98517137766, 0.53946065903),
+ (0.79374998808, 0.48124998808, 0.89375001192, 0.58125001192),
+ (0.77303934097, 0.38982862234, 0.91446065903, 0.67267137766),
+ (0.70232862234, 0.46053931117, 0.98517137766, 0.60196065903),
+ (0.79374998808, 0.54374998808, 0.89375001192, 0.64375001192),
+ (0.77303934097, 0.45232862234, 0.91446065903, 0.73517137766),
+ (0.70232862234, 0.52303934097, 0.98517137766, 0.66446065903),
+ (0.79374998808, 0.60624998808, 0.89375001192, 0.70625001192),
+ (0.77303934097, 0.51482862234, 0.91446065903, 0.79767137766),
+ (0.70232862234, 0.58553934097, 0.98517137766, 0.72696065903),
+ (0.79374998808, 0.66874998808, 0.89375001192, 0.76875001192),
+ (0.77303934097, 0.57732862234, 0.91446065903, 0.86017137766),
+ (0.70232862234, 0.64803934097, 0.98517137766, 0.78946065903),
+ (0.79374998808, 0.73124998808, 0.89375001192, 0.83125001192),
+ (0.77303934097, 0.63982862234, 0.91446065903, 0.92267137766),
+ (0.70232862234, 0.71053934097, 0.98517137766, 0.85196065903),
+ (0.79374998808, 0.79374998808, 0.89375001192, 0.89375001192),
+ (0.77303934097, 0.70232862234, 0.91446065903, 0.98517137766),
+ (0.70232862234, 0.77303934097, 0.98517137766, 0.91446065903),
+ (0.79374998808, 0.85624998808, 0.89375001192, 0.95625001192),
+ (0.77303934097, 0.76482862234, 0.91446065903, 1.0476713181),
+ (0.70232862234, 0.83553934097, 0.98517137766, 0.97696065903),
+ (0.79374998808, 0.91874998808, 0.89375001192, 1.0187499523),
+ (0.77303934097, 0.82732862234, 0.91446065903, 1.1101713181),
+ (0.70232862234, 0.89803934097, 0.98517137766, 1.039460659),
+ (0.85624998808, -0.018750000745, 0.95625001192, 0.08124999702),
+ (0.83553934097, -0.11017136276, 0.97696065903, 0.17267136276),
+ (0.76482862234, -0.039460681379, 1.0476713181, 0.10196068138),
+ (0.85624998808, 0.043749999255, 0.95625001192, 0.14374999702),
+ (0.83553934097, -0.047671362758, 0.97696065903, 0.23517136276),
+ (0.76482862234, 0.023039318621, 1.0476713181, 0.16446068883),
+ (0.85624998808, 0.10625000298, 0.95625001192, 0.20624999702),
+ (0.83553934097, 0.014828637242, 0.97696065903, 0.29767137766),
+ (0.76482862234, 0.085539318621, 1.0476713181, 0.22696068883),
+ (0.85624998808, 0.16875000298, 0.95625001192, 0.26875001192),
+ (0.83553934097, 0.077328637242, 0.97696065903, 0.36017137766),
+ (0.76482862234, 0.14803931117, 1.0476713181, 0.28946068883),
+ (0.85624998808, 0.23125000298, 0.95625001192, 0.33125001192),
+ (0.83553934097, 0.13982863724, 0.97696065903, 0.42267137766),
+ (0.76482862234, 0.21053931117, 1.0476713181, 0.35196068883),
+ (0.85624998808, 0.29374998808, 0.95625001192, 0.39375001192),
+ (0.83553934097, 0.20232863724, 0.97696065903, 0.48517137766),
+ (0.76482862234, 0.27303931117, 1.0476713181, 0.41446068883),
+ (0.85624998808, 0.35624998808, 0.95625001192, 0.45625001192),
+ (0.83553934097, 0.26482862234, 0.97696065903, 0.54767137766),
+ (0.76482862234, 0.33553931117, 1.0476713181, 0.47696068883),
+ (0.85624998808, 0.41874998808, 0.95625001192, 0.51875001192),
+ (0.83553934097, 0.32732862234, 0.97696065903, 0.61017137766),
+ (0.76482862234, 0.39803931117, 1.0476713181, 0.53946065903),
+ (0.85624998808, 0.48124998808, 0.95625001192, 0.58125001192),
+ (0.83553934097, 0.38982862234, 0.97696065903, 0.67267137766),
+ (0.76482862234, 0.46053931117, 1.0476713181, 0.60196065903),
+ (0.85624998808, 0.54374998808, 0.95625001192, 0.64375001192),
+ (0.83553934097, 0.45232862234, 0.97696065903, 0.73517137766),
+ (0.76482862234, 0.52303934097, 1.0476713181, 0.66446065903),
+ (0.85624998808, 0.60624998808, 0.95625001192, 0.70625001192),
+ (0.83553934097, 0.51482862234, 0.97696065903, 0.79767137766),
+ (0.76482862234, 0.58553934097, 1.0476713181, 0.72696065903),
+ (0.85624998808, 0.66874998808, 0.95625001192, 0.76875001192),
+ (0.83553934097, 0.57732862234, 0.97696065903, 0.86017137766),
+ (0.76482862234, 0.64803934097, 1.0476713181, 0.78946065903),
+ (0.85624998808, 0.73124998808, 0.95625001192, 0.83125001192),
+ (0.83553934097, 0.63982862234, 0.97696065903, 0.92267137766),
+ (0.76482862234, 0.71053934097, 1.0476713181, 0.85196065903),
+ (0.85624998808, 0.79374998808, 0.95625001192, 0.89375001192),
+ (0.83553934097, 0.70232862234, 0.97696065903, 0.98517137766),
+ (0.76482862234, 0.77303934097, 1.0476713181, 0.91446065903),
+ (0.85624998808, 0.85624998808, 0.95625001192, 0.95625001192),
+ (0.83553934097, 0.76482862234, 0.97696065903, 1.0476713181),
+ (0.76482862234, 0.83553934097, 1.0476713181, 0.97696065903),
+ (0.85624998808, 0.91874998808, 0.95625001192, 1.0187499523),
+ (0.83553934097, 0.82732862234, 0.97696065903, 1.1101713181),
+ (0.76482862234, 0.89803934097, 1.0476713181, 1.039460659),
+ (0.91874998808, -0.018750000745, 1.0187499523, 0.08124999702),
+ (0.89803934097, -0.11017136276, 1.039460659, 0.17267136276),
+ (0.82732862234, -0.039460681379, 1.1101713181, 0.10196068138),
+ (0.91874998808, 0.043749999255, 1.0187499523, 0.14374999702),
+ (0.89803934097, -0.047671362758, 1.039460659, 0.23517136276),
+ (0.82732862234, 0.023039318621, 1.1101713181, 0.16446068883),
+ (0.91874998808, 0.10625000298, 1.0187499523, 0.20624999702),
+ (0.89803934097, 0.014828637242, 1.039460659, 0.29767137766),
+ (0.82732862234, 0.085539318621, 1.1101713181, 0.22696068883),
+ (0.91874998808, 0.16875000298, 1.0187499523, 0.26875001192),
+ (0.89803934097, 0.077328637242, 1.039460659, 0.36017137766),
+ (0.82732862234, 0.14803931117, 1.1101713181, 0.28946068883),
+ (0.91874998808, 0.23125000298, 1.0187499523, 0.33125001192),
+ (0.89803934097, 0.13982863724, 1.039460659, 0.42267137766),
+ (0.82732862234, 0.21053931117, 1.1101713181, 0.35196068883),
+ (0.91874998808, 0.29374998808, 1.0187499523, 0.39375001192),
+ (0.89803934097, 0.20232863724, 1.039460659, 0.48517137766),
+ (0.82732862234, 0.27303931117, 1.1101713181, 0.41446068883),
+ (0.91874998808, 0.35624998808, 1.0187499523, 0.45625001192),
+ (0.89803934097, 0.26482862234, 1.039460659, 0.54767137766),
+ (0.82732862234, 0.33553931117, 1.1101713181, 0.47696068883),
+ (0.91874998808, 0.41874998808, 1.0187499523, 0.51875001192),
+ (0.89803934097, 0.32732862234, 1.039460659, 0.61017137766),
+ (0.82732862234, 0.39803931117, 1.1101713181, 0.53946065903),
+ (0.91874998808, 0.48124998808, 1.0187499523, 0.58125001192),
+ (0.89803934097, 0.38982862234, 1.039460659, 0.67267137766),
+ (0.82732862234, 0.46053931117, 1.1101713181, 0.60196065903),
+ (0.91874998808, 0.54374998808, 1.0187499523, 0.64375001192),
+ (0.89803934097, 0.45232862234, 1.039460659, 0.73517137766),
+ (0.82732862234, 0.52303934097, 1.1101713181, 0.66446065903),
+ (0.91874998808, 0.60624998808, 1.0187499523, 0.70625001192),
+ (0.89803934097, 0.51482862234, 1.039460659, 0.79767137766),
+ (0.82732862234, 0.58553934097, 1.1101713181, 0.72696065903),
+ (0.91874998808, 0.66874998808, 1.0187499523, 0.76875001192),
+ (0.89803934097, 0.57732862234, 1.039460659, 0.86017137766),
+ (0.82732862234, 0.64803934097, 1.1101713181, 0.78946065903),
+ (0.91874998808, 0.73124998808, 1.0187499523, 0.83125001192),
+ (0.89803934097, 0.63982862234, 1.039460659, 0.92267137766),
+ (0.82732862234, 0.71053934097, 1.1101713181, 0.85196065903),
+ (0.91874998808, 0.79374998808, 1.0187499523, 0.89375001192),
+ (0.89803934097, 0.70232862234, 1.039460659, 0.98517137766),
+ (0.82732862234, 0.77303934097, 1.1101713181, 0.91446065903),
+ (0.91874998808, 0.85624998808, 1.0187499523, 0.95625001192),
+ (0.89803934097, 0.76482862234, 1.039460659, 1.0476713181),
+ (0.82732862234, 0.83553934097, 1.1101713181, 0.97696065903),
+ (0.91874998808, 0.91874998808, 1.0187499523, 1.0187499523),
+ (0.89803934097, 0.82732862234, 1.039460659, 1.1101713181),
+ (0.82732862234, 0.89803934097, 1.1101713181, 1.039460659),
+ (-0.13125000894, -0.13124997914, 0.25625002384, 0.25624996424),
+ (-0.074501946568, -0.21150383353, 0.19950194657, 0.33650383353),
+ (-0.21150389314, -0.074501916766, 0.33650389314, 0.19950191677),
+ (-0.049361616373, -0.27308481932, 0.17436161637, 0.39808481932),
+ (-0.27310159802, -0.049356020987, 0.39810159802, 0.17435601354),
+ (-0.17351509631, -0.17351509631, 0.29851508141, 0.29851508141),
+ (-0.13125000894, -0.0062499791384, 0.25625002384, 0.38124996424),
+ (-0.074501946568, -0.086503833532, 0.19950194657, 0.46150383353),
+ (-0.21150389314, 0.050498083234, 0.33650389314, 0.32450193167),
+ (-0.049361616373, -0.14808481932, 0.17436161637, 0.52308481932),
+ (-0.27310159802, 0.075643979013, 0.39810159802, 0.29935601354),
+ (-0.17351509631, -0.048515096307, 0.29851508141, 0.42351508141),
+ (-0.13125000894, 0.11875002086, 0.25625002384, 0.50624996424),
+ (-0.074501946568, 0.038496166468, 0.19950194657, 0.58650386333),
+ (-0.21150389314, 0.17549808323, 0.33650389314, 0.44950193167),
+ (-0.049361616373, -0.023084819317, 0.17436161637, 0.64808481932),
+ (-0.27310159802, 0.20064398646, 0.39810159802, 0.42435601354),
+ (-0.17351509631, 0.076484903693, 0.29851508141, 0.54851508141),
+ (-0.13125000894, 0.24375002086, 0.25625002384, 0.63124996424),
+ (-0.074501946568, 0.16349616647, 0.19950194657, 0.71150386333),
+ (-0.21150389314, 0.30049806833, 0.33650389314, 0.57450193167),
+ (-0.049361616373, 0.10191518068, 0.17436161637, 0.77308481932),
+ (-0.27310159802, 0.32564398646, 0.39810159802, 0.54935604334),
+ (-0.17351509631, 0.20148490369, 0.29851508141, 0.67351508141),
+ (-0.13125000894, 0.36875003576, 0.25625002384, 0.75624996424),
+ (-0.074501946568, 0.28849616647, 0.19950194657, 0.83650386333),
+ (-0.21150389314, 0.42549806833, 0.33650389314, 0.69950193167),
+ (-0.049361616373, 0.22691518068, 0.17436161637, 0.89808481932),
+ (-0.27310159802, 0.45064398646, 0.39810159802, 0.67435604334),
+ (-0.17351509631, 0.32648491859, 0.29851508141, 0.79851508141),
+ (-0.13125000894, 0.49375003576, 0.25625002384, 0.88124996424),
+ (-0.074501946568, 0.41349616647, 0.19950194657, 0.96150386333),
+ (-0.21150389314, 0.55049806833, 0.33650389314, 0.82450193167),
+ (-0.049361616373, 0.35191518068, 0.17436161637, 1.0230848789),
+ (-0.27310159802, 0.57564395666, 0.39810159802, 0.79935604334),
+ (-0.17351509631, 0.45148491859, 0.29851508141, 0.92351508141),
+ (-0.13125000894, 0.61875003576, 0.25625002384, 1.0062500238),
+ (-0.074501946568, 0.53849613667, 0.19950194657, 1.0865038633),
+ (-0.21150389314, 0.67549806833, 0.33650389314, 0.94950193167),
+ (-0.049361616373, 0.47691518068, 0.17436161637, 1.1480848789),
+ (-0.27310159802, 0.70064395666, 0.39810159802, 0.92435604334),
+ (-0.17351509631, 0.57648491859, 0.29851508141, 1.0485150814),
+ (-0.13125000894, 0.74375003576, 0.25625002384, 1.1312500238),
+ (-0.074501946568, 0.66349613667, 0.19950194657, 1.2115038633),
+ (-0.21150389314, 0.80049806833, 0.33650389314, 1.0745018721),
+ (-0.049361616373, 0.60191518068, 0.17436161637, 1.2730848789),
+ (-0.27310159802, 0.82564395666, 0.39810159802, 1.0493559837),
+ (-0.17351509631, 0.70148491859, 0.29851508141, 1.1735150814),
+ (-0.0062500089407, -0.13124997914, 0.38125002384, 0.25624996424),
+ (0.050498053432, -0.21150383353, 0.32450193167, 0.33650383353),
+ (-0.086503893137, -0.074501916766, 0.46150389314, 0.19950191677),
+ (0.075638383627, -0.27308481932, 0.29936161637, 0.39808481932),
+ (-0.14810159802, -0.049356020987, 0.52310156822, 0.17435601354),
+ (-0.048515096307, -0.17351509631, 0.42351508141, 0.29851508141),
+ (-0.0062500089407, -0.0062499791384, 0.38125002384, 0.38124996424),
+ (0.050498053432, -0.086503833532, 0.32450193167, 0.46150383353),
+ (-0.086503893137, 0.050498083234, 0.46150389314, 0.32450193167),
+ (0.075638383627, -0.14808481932, 0.29936161637, 0.52308481932),
+ (-0.14810159802, 0.075643979013, 0.52310156822, 0.29935601354),
+ (-0.048515096307, -0.048515096307, 0.42351508141, 0.42351508141),
+ (-0.0062500089407, 0.11875002086, 0.38125002384, 0.50624996424),
+ (0.050498053432, 0.038496166468, 0.32450193167, 0.58650386333),
+ (-0.086503893137, 0.17549808323, 0.46150389314, 0.44950193167),
+ (0.075638383627, -0.023084819317, 0.29936161637, 0.64808481932),
+ (-0.14810159802, 0.20064398646, 0.52310156822, 0.42435601354),
+ (-0.048515096307, 0.076484903693, 0.42351508141, 0.54851508141),
+ (-0.0062500089407, 0.24375002086, 0.38125002384, 0.63124996424),
+ (0.050498053432, 0.16349616647, 0.32450193167, 0.71150386333),
+ (-0.086503893137, 0.30049806833, 0.46150389314, 0.57450193167),
+ (0.075638383627, 0.10191518068, 0.29936161637, 0.77308481932),
+ (-0.14810159802, 0.32564398646, 0.52310156822, 0.54935604334),
+ (-0.048515096307, 0.20148490369, 0.42351508141, 0.67351508141),
+ (-0.0062500089407, 0.36875003576, 0.38125002384, 0.75624996424),
+ (0.050498053432, 0.28849616647, 0.32450193167, 0.83650386333),
+ (-0.086503893137, 0.42549806833, 0.46150389314, 0.69950193167),
+ (0.075638383627, 0.22691518068, 0.29936161637, 0.89808481932),
+ (-0.14810159802, 0.45064398646, 0.52310156822, 0.67435604334),
+ (-0.048515096307, 0.32648491859, 0.42351508141, 0.79851508141),
+ (-0.0062500089407, 0.49375003576, 0.38125002384, 0.88124996424),
+ (0.050498053432, 0.41349616647, 0.32450193167, 0.96150386333),
+ (-0.086503893137, 0.55049806833, 0.46150389314, 0.82450193167),
+ (0.075638383627, 0.35191518068, 0.29936161637, 1.0230848789),
+ (-0.14810159802, 0.57564395666, 0.52310156822, 0.79935604334),
+ (-0.048515096307, 0.45148491859, 0.42351508141, 0.92351508141),
+ (-0.0062500089407, 0.61875003576, 0.38125002384, 1.0062500238),
+ (0.050498053432, 0.53849613667, 0.32450193167, 1.0865038633),
+ (-0.086503893137, 0.67549806833, 0.46150389314, 0.94950193167),
+ (0.075638383627, 0.47691518068, 0.29936161637, 1.1480848789),
+ (-0.14810159802, 0.70064395666, 0.52310156822, 0.92435604334),
+ (-0.048515096307, 0.57648491859, 0.42351508141, 1.0485150814),
+ (-0.0062500089407, 0.74375003576, 0.38125002384, 1.1312500238),
+ (0.050498053432, 0.66349613667, 0.32450193167, 1.2115038633),
+ (-0.086503893137, 0.80049806833, 0.46150389314, 1.0745018721),
+ (0.075638383627, 0.60191518068, 0.29936161637, 1.2730848789),
+ (-0.14810159802, 0.82564395666, 0.52310156822, 1.0493559837),
+ (-0.048515096307, 0.70148491859, 0.42351508141, 1.1735150814),
+ (0.11874999106, -0.13124997914, 0.50625002384, 0.25624996424),
+ (0.17549805343, -0.21150383353, 0.44950193167, 0.33650383353),
+ (0.038496106863, -0.074501916766, 0.58650386333, 0.19950191677),
+ (0.20063838363, -0.27308481932, 0.42436161637, 0.39808481932),
+ (-0.023101598024, -0.049356020987, 0.64810156822, 0.17435601354),
+ (0.076484903693, -0.17351509631, 0.54851508141, 0.29851508141),
+ (0.11874999106, -0.0062499791384, 0.50625002384, 0.38124996424),
+ (0.17549805343, -0.086503833532, 0.44950193167, 0.46150383353),
+ (0.038496106863, 0.050498083234, 0.58650386333, 0.32450193167),
+ (0.20063838363, -0.14808481932, 0.42436161637, 0.52308481932),
+ (-0.023101598024, 0.075643979013, 0.64810156822, 0.29935601354),
+ (0.076484903693, -0.048515096307, 0.54851508141, 0.42351508141),
+ (0.11874999106, 0.11875002086, 0.50625002384, 0.50624996424),
+ (0.17549805343, 0.038496166468, 0.44950193167, 0.58650386333),
+ (0.038496106863, 0.17549808323, 0.58650386333, 0.44950193167),
+ (0.20063838363, -0.023084819317, 0.42436161637, 0.64808481932),
+ (-0.023101598024, 0.20064398646, 0.64810156822, 0.42435601354),
+ (0.076484903693, 0.076484903693, 0.54851508141, 0.54851508141),
+ (0.11874999106, 0.24375002086, 0.50625002384, 0.63124996424),
+ (0.17549805343, 0.16349616647, 0.44950193167, 0.71150386333),
+ (0.038496106863, 0.30049806833, 0.58650386333, 0.57450193167),
+ (0.20063838363, 0.10191518068, 0.42436161637, 0.77308481932),
+ (-0.023101598024, 0.32564398646, 0.64810156822, 0.54935604334),
+ (0.076484903693, 0.20148490369, 0.54851508141, 0.67351508141),
+ (0.11874999106, 0.36875003576, 0.50625002384, 0.75624996424),
+ (0.17549805343, 0.28849616647, 0.44950193167, 0.83650386333),
+ (0.038496106863, 0.42549806833, 0.58650386333, 0.69950193167),
+ (0.20063838363, 0.22691518068, 0.42436161637, 0.89808481932),
+ (-0.023101598024, 0.45064398646, 0.64810156822, 0.67435604334),
+ (0.076484903693, 0.32648491859, 0.54851508141, 0.79851508141),
+ (0.11874999106, 0.49375003576, 0.50625002384, 0.88124996424),
+ (0.17549805343, 0.41349616647, 0.44950193167, 0.96150386333),
+ (0.038496106863, 0.55049806833, 0.58650386333, 0.82450193167),
+ (0.20063838363, 0.35191518068, 0.42436161637, 1.0230848789),
+ (-0.023101598024, 0.57564395666, 0.64810156822, 0.79935604334),
+ (0.076484903693, 0.45148491859, 0.54851508141, 0.92351508141),
+ (0.11874999106, 0.61875003576, 0.50625002384, 1.0062500238),
+ (0.17549805343, 0.53849613667, 0.44950193167, 1.0865038633),
+ (0.038496106863, 0.67549806833, 0.58650386333, 0.94950193167),
+ (0.20063838363, 0.47691518068, 0.42436161637, 1.1480848789),
+ (-0.023101598024, 0.70064395666, 0.64810156822, 0.92435604334),
+ (0.076484903693, 0.57648491859, 0.54851508141, 1.0485150814),
+ (0.11874999106, 0.74375003576, 0.50625002384, 1.1312500238),
+ (0.17549805343, 0.66349613667, 0.44950193167, 1.2115038633),
+ (0.038496106863, 0.80049806833, 0.58650386333, 1.0745018721),
+ (0.20063838363, 0.60191518068, 0.42436161637, 1.2730848789),
+ (-0.023101598024, 0.82564395666, 0.64810156822, 1.0493559837),
+ (0.076484903693, 0.70148491859, 0.54851508141, 1.1735150814),
+ (0.24374999106, -0.13124997914, 0.63125002384, 0.25624996424),
+ (0.30049806833, -0.21150383353, 0.57450193167, 0.33650383353),
+ (0.16349610686, -0.074501916766, 0.71150386333, 0.19950191677),
+ (0.32563838363, -0.27308481932, 0.54936158657, 0.39808481932),
+ (0.10189840198, -0.049356020987, 0.77310156822, 0.17435601354),
+ (0.20148490369, -0.17351509631, 0.67351508141, 0.29851508141),
+ (0.24374999106, -0.0062499791384, 0.63125002384, 0.38124996424),
+ (0.30049806833, -0.086503833532, 0.57450193167, 0.46150383353),
+ (0.16349610686, 0.050498083234, 0.71150386333, 0.32450193167),
+ (0.32563838363, -0.14808481932, 0.54936158657, 0.52308481932),
+ (0.10189840198, 0.075643979013, 0.77310156822, 0.29935601354),
+ (0.20148490369, -0.048515096307, 0.67351508141, 0.42351508141),
+ (0.24374999106, 0.11875002086, 0.63125002384, 0.50624996424),
+ (0.30049806833, 0.038496166468, 0.57450193167, 0.58650386333),
+ (0.16349610686, 0.17549808323, 0.71150386333, 0.44950193167),
+ (0.32563838363, -0.023084819317, 0.54936158657, 0.64808481932),
+ (0.10189840198, 0.20064398646, 0.77310156822, 0.42435601354),
+ (0.20148490369, 0.076484903693, 0.67351508141, 0.54851508141),
+ (0.24374999106, 0.24375002086, 0.63125002384, 0.63124996424),
+ (0.30049806833, 0.16349616647, 0.57450193167, 0.71150386333),
+ (0.16349610686, 0.30049806833, 0.71150386333, 0.57450193167),
+ (0.32563838363, 0.10191518068, 0.54936158657, 0.77308481932),
+ (0.10189840198, 0.32564398646, 0.77310156822, 0.54935604334),
+ (0.20148490369, 0.20148490369, 0.67351508141, 0.67351508141),
+ (0.24374999106, 0.36875003576, 0.63125002384, 0.75624996424),
+ (0.30049806833, 0.28849616647, 0.57450193167, 0.83650386333),
+ (0.16349610686, 0.42549806833, 0.71150386333, 0.69950193167),
+ (0.32563838363, 0.22691518068, 0.54936158657, 0.89808481932),
+ (0.10189840198, 0.45064398646, 0.77310156822, 0.67435604334),
+ (0.20148490369, 0.32648491859, 0.67351508141, 0.79851508141),
+ (0.24374999106, 0.49375003576, 0.63125002384, 0.88124996424),
+ (0.30049806833, 0.41349616647, 0.57450193167, 0.96150386333),
+ (0.16349610686, 0.55049806833, 0.71150386333, 0.82450193167),
+ (0.32563838363, 0.35191518068, 0.54936158657, 1.0230848789),
+ (0.10189840198, 0.57564395666, 0.77310156822, 0.79935604334),
+ (0.20148490369, 0.45148491859, 0.67351508141, 0.92351508141),
+ (0.24374999106, 0.61875003576, 0.63125002384, 1.0062500238),
+ (0.30049806833, 0.53849613667, 0.57450193167, 1.0865038633),
+ (0.16349610686, 0.67549806833, 0.71150386333, 0.94950193167),
+ (0.32563838363, 0.47691518068, 0.54936158657, 1.1480848789),
+ (0.10189840198, 0.70064395666, 0.77310156822, 0.92435604334),
+ (0.20148490369, 0.57648491859, 0.67351508141, 1.0485150814),
+ (0.24374999106, 0.74375003576, 0.63125002384, 1.1312500238),
+ (0.30049806833, 0.66349613667, 0.57450193167, 1.2115038633),
+ (0.16349610686, 0.80049806833, 0.71150386333, 1.0745018721),
+ (0.32563838363, 0.60191518068, 0.54936158657, 1.2730848789),
+ (0.10189840198, 0.82564395666, 0.77310156822, 1.0493559837),
+ (0.20148490369, 0.70148491859, 0.67351508141, 1.1735150814),
+ (0.36874997616, -0.13124997914, 0.75625002384, 0.25624996424),
+ (0.42549806833, -0.21150383353, 0.69950193167, 0.33650383353),
+ (0.28849610686, -0.074501916766, 0.83650386333, 0.19950191677),
+ (0.45063838363, -0.27308481932, 0.67436158657, 0.39808481932),
+ (0.22689840198, -0.049356020987, 0.89810156822, 0.17435601354),
+ (0.32648491859, -0.17351509631, 0.79851508141, 0.29851508141),
+ (0.36874997616, -0.0062499791384, 0.75625002384, 0.38124996424),
+ (0.42549806833, -0.086503833532, 0.69950193167, 0.46150383353),
+ (0.28849610686, 0.050498083234, 0.83650386333, 0.32450193167),
+ (0.45063838363, -0.14808481932, 0.67436158657, 0.52308481932),
+ (0.22689840198, 0.075643979013, 0.89810156822, 0.29935601354),
+ (0.32648491859, -0.048515096307, 0.79851508141, 0.42351508141),
+ (0.36874997616, 0.11875002086, 0.75625002384, 0.50624996424),
+ (0.42549806833, 0.038496166468, 0.69950193167, 0.58650386333),
+ (0.28849610686, 0.17549808323, 0.83650386333, 0.44950193167),
+ (0.45063838363, -0.023084819317, 0.67436158657, 0.64808481932),
+ (0.22689840198, 0.20064398646, 0.89810156822, 0.42435601354),
+ (0.32648491859, 0.076484903693, 0.79851508141, 0.54851508141),
+ (0.36874997616, 0.24375002086, 0.75625002384, 0.63124996424),
+ (0.42549806833, 0.16349616647, 0.69950193167, 0.71150386333),
+ (0.28849610686, 0.30049806833, 0.83650386333, 0.57450193167),
+ (0.45063838363, 0.10191518068, 0.67436158657, 0.77308481932),
+ (0.22689840198, 0.32564398646, 0.89810156822, 0.54935604334),
+ (0.32648491859, 0.20148490369, 0.79851508141, 0.67351508141),
+ (0.36874997616, 0.36875003576, 0.75625002384, 0.75624996424),
+ (0.42549806833, 0.28849616647, 0.69950193167, 0.83650386333),
+ (0.28849610686, 0.42549806833, 0.83650386333, 0.69950193167),
+ (0.45063838363, 0.22691518068, 0.67436158657, 0.89808481932),
+ (0.22689840198, 0.45064398646, 0.89810156822, 0.67435604334),
+ (0.32648491859, 0.32648491859, 0.79851508141, 0.79851508141),
+ (0.36874997616, 0.49375003576, 0.75625002384, 0.88124996424),
+ (0.42549806833, 0.41349616647, 0.69950193167, 0.96150386333),
+ (0.28849610686, 0.55049806833, 0.83650386333, 0.82450193167),
+ (0.45063838363, 0.35191518068, 0.67436158657, 1.0230848789),
+ (0.22689840198, 0.57564395666, 0.89810156822, 0.79935604334),
+ (0.32648491859, 0.45148491859, 0.79851508141, 0.92351508141),
+ (0.36874997616, 0.61875003576, 0.75625002384, 1.0062500238),
+ (0.42549806833, 0.53849613667, 0.69950193167, 1.0865038633),
+ (0.28849610686, 0.67549806833, 0.83650386333, 0.94950193167),
+ (0.45063838363, 0.47691518068, 0.67436158657, 1.1480848789),
+ (0.22689840198, 0.70064395666, 0.89810156822, 0.92435604334),
+ (0.32648491859, 0.57648491859, 0.79851508141, 1.0485150814),
+ (0.36874997616, 0.74375003576, 0.75625002384, 1.1312500238),
+ (0.42549806833, 0.66349613667, 0.69950193167, 1.2115038633),
+ (0.28849610686, 0.80049806833, 0.83650386333, 1.0745018721),
+ (0.45063838363, 0.60191518068, 0.67436158657, 1.2730848789),
+ (0.22689840198, 0.82564395666, 0.89810156822, 1.0493559837),
+ (0.32648491859, 0.70148491859, 0.79851508141, 1.1735150814),
+ (0.49374997616, -0.13124997914, 0.88125002384, 0.25624996424),
+ (0.55049806833, -0.21150383353, 0.82450193167, 0.33650383353),
+ (0.41349610686, -0.074501916766, 0.96150386333, 0.19950191677),
+ (0.57563841343, -0.27308481932, 0.79936158657, 0.39808481932),
+ (0.35189840198, -0.049356020987, 1.0231015682, 0.17435601354),
+ (0.45148491859, -0.17351509631, 0.92351508141, 0.29851508141),
+ (0.49374997616, -0.0062499791384, 0.88125002384, 0.38124996424),
+ (0.55049806833, -0.086503833532, 0.82450193167, 0.46150383353),
+ (0.41349610686, 0.050498083234, 0.96150386333, 0.32450193167),
+ (0.57563841343, -0.14808481932, 0.79936158657, 0.52308481932),
+ (0.35189840198, 0.075643979013, 1.0231015682, 0.29935601354),
+ (0.45148491859, -0.048515096307, 0.92351508141, 0.42351508141),
+ (0.49374997616, 0.11875002086, 0.88125002384, 0.50624996424),
+ (0.55049806833, 0.038496166468, 0.82450193167, 0.58650386333),
+ (0.41349610686, 0.17549808323, 0.96150386333, 0.44950193167),
+ (0.57563841343, -0.023084819317, 0.79936158657, 0.64808481932),
+ (0.35189840198, 0.20064398646, 1.0231015682, 0.42435601354),
+ (0.45148491859, 0.076484903693, 0.92351508141, 0.54851508141),
+ (0.49374997616, 0.24375002086, 0.88125002384, 0.63124996424),
+ (0.55049806833, 0.16349616647, 0.82450193167, 0.71150386333),
+ (0.41349610686, 0.30049806833, 0.96150386333, 0.57450193167),
+ (0.57563841343, 0.10191518068, 0.79936158657, 0.77308481932),
+ (0.35189840198, 0.32564398646, 1.0231015682, 0.54935604334),
+ (0.45148491859, 0.20148490369, 0.92351508141, 0.67351508141),
+ (0.49374997616, 0.36875003576, 0.88125002384, 0.75624996424),
+ (0.55049806833, 0.28849616647, 0.82450193167, 0.83650386333),
+ (0.41349610686, 0.42549806833, 0.96150386333, 0.69950193167),
+ (0.57563841343, 0.22691518068, 0.79936158657, 0.89808481932),
+ (0.35189840198, 0.45064398646, 1.0231015682, 0.67435604334),
+ (0.45148491859, 0.32648491859, 0.92351508141, 0.79851508141),
+ (0.49374997616, 0.49375003576, 0.88125002384, 0.88124996424),
+ (0.55049806833, 0.41349616647, 0.82450193167, 0.96150386333),
+ (0.41349610686, 0.55049806833, 0.96150386333, 0.82450193167),
+ (0.57563841343, 0.35191518068, 0.79936158657, 1.0230848789),
+ (0.35189840198, 0.57564395666, 1.0231015682, 0.79935604334),
+ (0.45148491859, 0.45148491859, 0.92351508141, 0.92351508141),
+ (0.49374997616, 0.61875003576, 0.88125002384, 1.0062500238),
+ (0.55049806833, 0.53849613667, 0.82450193167, 1.0865038633),
+ (0.41349610686, 0.67549806833, 0.96150386333, 0.94950193167),
+ (0.57563841343, 0.47691518068, 0.79936158657, 1.1480848789),
+ (0.35189840198, 0.70064395666, 1.0231015682, 0.92435604334),
+ (0.45148491859, 0.57648491859, 0.92351508141, 1.0485150814),
+ (0.49374997616, 0.74375003576, 0.88125002384, 1.1312500238),
+ (0.55049806833, 0.66349613667, 0.82450193167, 1.2115038633),
+ (0.41349610686, 0.80049806833, 0.96150386333, 1.0745018721),
+ (0.57563841343, 0.60191518068, 0.79936158657, 1.2730848789),
+ (0.35189840198, 0.82564395666, 1.0231015682, 1.0493559837),
+ (0.45148491859, 0.70148491859, 0.92351508141, 1.1735150814),
+ (0.61874997616, -0.13124997914, 1.0062500238, 0.25624996424),
+ (0.67549806833, -0.21150383353, 0.94950193167, 0.33650383353),
+ (0.53849613667, -0.074501916766, 1.0865038633, 0.19950191677),
+ (0.70063841343, -0.27308481932, 0.92436158657, 0.39808481932),
+ (0.47689840198, -0.049356020987, 1.1481015682, 0.17435601354),
+ (0.57648491859, -0.17351509631, 1.0485150814, 0.29851508141),
+ (0.61874997616, -0.0062499791384, 1.0062500238, 0.38124996424),
+ (0.67549806833, -0.086503833532, 0.94950193167, 0.46150383353),
+ (0.53849613667, 0.050498083234, 1.0865038633, 0.32450193167),
+ (0.70063841343, -0.14808481932, 0.92436158657, 0.52308481932),
+ (0.47689840198, 0.075643979013, 1.1481015682, 0.29935601354),
+ (0.57648491859, -0.048515096307, 1.0485150814, 0.42351508141),
+ (0.61874997616, 0.11875002086, 1.0062500238, 0.50624996424),
+ (0.67549806833, 0.038496166468, 0.94950193167, 0.58650386333),
+ (0.53849613667, 0.17549808323, 1.0865038633, 0.44950193167),
+ (0.70063841343, -0.023084819317, 0.92436158657, 0.64808481932),
+ (0.47689840198, 0.20064398646, 1.1481015682, 0.42435601354),
+ (0.57648491859, 0.076484903693, 1.0485150814, 0.54851508141),
+ (0.61874997616, 0.24375002086, 1.0062500238, 0.63124996424),
+ (0.67549806833, 0.16349616647, 0.94950193167, 0.71150386333),
+ (0.53849613667, 0.30049806833, 1.0865038633, 0.57450193167),
+ (0.70063841343, 0.10191518068, 0.92436158657, 0.77308481932),
+ (0.47689840198, 0.32564398646, 1.1481015682, 0.54935604334),
+ (0.57648491859, 0.20148490369, 1.0485150814, 0.67351508141),
+ (0.61874997616, 0.36875003576, 1.0062500238, 0.75624996424),
+ (0.67549806833, 0.28849616647, 0.94950193167, 0.83650386333),
+ (0.53849613667, 0.42549806833, 1.0865038633, 0.69950193167),
+ (0.70063841343, 0.22691518068, 0.92436158657, 0.89808481932),
+ (0.47689840198, 0.45064398646, 1.1481015682, 0.67435604334),
+ (0.57648491859, 0.32648491859, 1.0485150814, 0.79851508141),
+ (0.61874997616, 0.49375003576, 1.0062500238, 0.88124996424),
+ (0.67549806833, 0.41349616647, 0.94950193167, 0.96150386333),
+ (0.53849613667, 0.55049806833, 1.0865038633, 0.82450193167),
+ (0.70063841343, 0.35191518068, 0.92436158657, 1.0230848789),
+ (0.47689840198, 0.57564395666, 1.1481015682, 0.79935604334),
+ (0.57648491859, 0.45148491859, 1.0485150814, 0.92351508141),
+ (0.61874997616, 0.61875003576, 1.0062500238, 1.0062500238),
+ (0.67549806833, 0.53849613667, 0.94950193167, 1.0865038633),
+ (0.53849613667, 0.67549806833, 1.0865038633, 0.94950193167),
+ (0.70063841343, 0.47691518068, 0.92436158657, 1.1480848789),
+ (0.47689840198, 0.70064395666, 1.1481015682, 0.92435604334),
+ (0.57648491859, 0.57648491859, 1.0485150814, 1.0485150814),
+ (0.61874997616, 0.74375003576, 1.0062500238, 1.1312500238),
+ (0.67549806833, 0.66349613667, 0.94950193167, 1.2115038633),
+ (0.53849613667, 0.80049806833, 1.0865038633, 1.0745018721),
+ (0.70063841343, 0.60191518068, 0.92436158657, 1.2730848789),
+ (0.47689840198, 0.82564395666, 1.1481015682, 1.0493559837),
+ (0.57648491859, 0.70148491859, 1.0485150814, 1.1735150814),
+ (0.74374997616, -0.13124997914, 1.1312500238, 0.25624996424),
+ (0.80049806833, -0.21150383353, 1.0745019913, 0.33650383353),
+ (0.66349613667, -0.074501916766, 1.2115038633, 0.19950191677),
+ (0.82563841343, -0.27308481932, 1.0493615866, 0.39808481932),
+ (0.60189843178, -0.049356020987, 1.2731015682, 0.17435601354),
+ (0.70148491859, -0.17351509631, 1.1735150814, 0.29851508141),
+ (0.74374997616, -0.0062499791384, 1.1312500238, 0.38124996424),
+ (0.80049806833, -0.086503833532, 1.0745019913, 0.46150383353),
+ (0.66349613667, 0.050498083234, 1.2115038633, 0.32450193167),
+ (0.82563841343, -0.14808481932, 1.0493615866, 0.52308481932),
+ (0.60189843178, 0.075643979013, 1.2731015682, 0.29935601354),
+ (0.70148491859, -0.048515096307, 1.1735150814, 0.42351508141),
+ (0.74374997616, 0.11875002086, 1.1312500238, 0.50624996424),
+ (0.80049806833, 0.038496166468, 1.0745019913, 0.58650386333),
+ (0.66349613667, 0.17549808323, 1.2115038633, 0.44950193167),
+ (0.82563841343, -0.023084819317, 1.0493615866, 0.64808481932),
+ (0.60189843178, 0.20064398646, 1.2731015682, 0.42435601354),
+ (0.70148491859, 0.076484903693, 1.1735150814, 0.54851508141),
+ (0.74374997616, 0.24375002086, 1.1312500238, 0.63124996424),
+ (0.80049806833, 0.16349616647, 1.0745019913, 0.71150386333),
+ (0.66349613667, 0.30049806833, 1.2115038633, 0.57450193167),
+ (0.82563841343, 0.10191518068, 1.0493615866, 0.77308481932),
+ (0.60189843178, 0.32564398646, 1.2731015682, 0.54935604334),
+ (0.70148491859, 0.20148490369, 1.1735150814, 0.67351508141),
+ (0.74374997616, 0.36875003576, 1.1312500238, 0.75624996424),
+ (0.80049806833, 0.28849616647, 1.0745019913, 0.83650386333),
+ (0.66349613667, 0.42549806833, 1.2115038633, 0.69950193167),
+ (0.82563841343, 0.22691518068, 1.0493615866, 0.89808481932),
+ (0.60189843178, 0.45064398646, 1.2731015682, 0.67435604334),
+ (0.70148491859, 0.32648491859, 1.1735150814, 0.79851508141),
+ (0.74374997616, 0.49375003576, 1.1312500238, 0.88124996424),
+ (0.80049806833, 0.41349616647, 1.0745019913, 0.96150386333),
+ (0.66349613667, 0.55049806833, 1.2115038633, 0.82450193167),
+ (0.82563841343, 0.35191518068, 1.0493615866, 1.0230848789),
+ (0.60189843178, 0.57564395666, 1.2731015682, 0.79935604334),
+ (0.70148491859, 0.45148491859, 1.1735150814, 0.92351508141),
+ (0.74374997616, 0.61875003576, 1.1312500238, 1.0062500238),
+ (0.80049806833, 0.53849613667, 1.0745019913, 1.0865038633),
+ (0.66349613667, 0.67549806833, 1.2115038633, 0.94950193167),
+ (0.82563841343, 0.47691518068, 1.0493615866, 1.1480848789),
+ (0.60189843178, 0.70064395666, 1.2731015682, 0.92435604334),
+ (0.70148491859, 0.57648491859, 1.1735150814, 1.0485150814),
+ (0.74374997616, 0.74375003576, 1.1312500238, 1.1312500238),
+ (0.80049806833, 0.66349613667, 1.0745019913, 1.2115038633),
+ (0.66349613667, 0.80049806833, 1.2115038633, 1.0745018721),
+ (0.82563841343, 0.60191518068, 1.0493615866, 1.2730848789),
+ (0.60189843178, 0.82564395666, 1.2731015682, 1.0493559837),
+ (0.70148491859, 0.70148491859, 1.1735150814, 1.1735150814),
+ (-0.16250002384, -0.16249996424, 0.41250002384, 0.41249996424),
+ (-0.078293219209, -0.28158634901, 0.32829320431, 0.53158634901),
+ (-0.28158643842, -0.078293174505, 0.53158640862, 0.32829317451),
+ (-0.040988206863, -0.37296459079, 0.29098820686, 0.62296462059),
+ (-0.37298947573, -0.040979906917, 0.62298947573, 0.29097992182),
+ (-0.20607307553, -0.20607307553, 0.45607307553, 0.45607307553),
+ (-0.16250002384, 0.087500035763, 0.41250002384, 0.66249996424),
+ (-0.078293219209, -0.03158634901, 0.32829320431, 0.78158634901),
+ (-0.28158643842, 0.17170682549, 0.53158640862, 0.57829320431),
+ (-0.040988206863, -0.12296459079, 0.29098820686, 0.87296462059),
+ (-0.37298947573, 0.20902009308, 0.62298947573, 0.54097992182),
+ (-0.20607307553, 0.043926924467, 0.45607307553, 0.70607304573),
+ (-0.16250002384, 0.33750003576, 0.41250002384, 0.91249996424),
+ (-0.078293219209, 0.21841365099, 0.32829320431, 1.0315864086),
+ (-0.28158643842, 0.42170682549, 0.53158640862, 0.82829320431),
+ (-0.040988206863, 0.12703540921, 0.29098820686, 1.1229646206),
+ (-0.37298947573, 0.45902007818, 0.62298947573, 0.79097992182),
+ (-0.20607307553, 0.29392692447, 0.45607307553, 0.95607304573),
+ (-0.16250002384, 0.58750003576, 0.41250002384, 1.1624999046),
+ (-0.078293219209, 0.46841365099, 0.32829320431, 1.2815864086),
+ (-0.28158643842, 0.67170679569, 0.53158640862, 1.0782932043),
+ (-0.040988206863, 0.37703540921, 0.29098820686, 1.3729646206),
+ (-0.37298947573, 0.70902007818, 0.62298947573, 1.0409798622),
+ (-0.20607307553, 0.54392695427, 0.45607307553, 1.2060730457),
+ (0.087499976158, -0.16249996424, 0.66250002384, 0.41249996424),
+ (0.17170678079, -0.28158634901, 0.57829320431, 0.53158634901),
+ (-0.031586438417, -0.078293174505, 0.78158640862, 0.32829317451),
+ (0.20901179314, -0.37296459079, 0.54098820686, 0.62296462059),
+ (-0.12298947573, -0.040979906917, 0.87298947573, 0.29097992182),
+ (0.043926924467, -0.20607307553, 0.70607304573, 0.45607307553),
+ (0.087499976158, 0.087500035763, 0.66250002384, 0.66249996424),
+ (0.17170678079, -0.03158634901, 0.57829320431, 0.78158634901),
+ (-0.031586438417, 0.17170682549, 0.78158640862, 0.57829320431),
+ (0.20901179314, -0.12296459079, 0.54098820686, 0.87296462059),
+ (-0.12298947573, 0.20902009308, 0.87298947573, 0.54097992182),
+ (0.043926924467, 0.043926924467, 0.70607304573, 0.70607304573),
+ (0.087499976158, 0.33750003576, 0.66250002384, 0.91249996424),
+ (0.17170678079, 0.21841365099, 0.57829320431, 1.0315864086),
+ (-0.031586438417, 0.42170682549, 0.78158640862, 0.82829320431),
+ (0.20901179314, 0.12703540921, 0.54098820686, 1.1229646206),
+ (-0.12298947573, 0.45902007818, 0.87298947573, 0.79097992182),
+ (0.043926924467, 0.29392692447, 0.70607304573, 0.95607304573),
+ (0.087499976158, 0.58750003576, 0.66250002384, 1.1624999046),
+ (0.17170678079, 0.46841365099, 0.57829320431, 1.2815864086),
+ (-0.031586438417, 0.67170679569, 0.78158640862, 1.0782932043),
+ (0.20901179314, 0.37703540921, 0.54098820686, 1.3729646206),
+ (-0.12298947573, 0.70902007818, 0.87298947573, 1.0409798622),
+ (0.043926924467, 0.54392695427, 0.70607304573, 1.2060730457),
+ (0.33749997616, -0.16249996424, 0.91250002384, 0.41249996424),
+ (0.42170679569, -0.28158634901, 0.82829320431, 0.53158634901),
+ (0.21841356158, -0.078293174505, 1.0315864086, 0.32829317451),
+ (0.45901179314, -0.37296459079, 0.79098820686, 0.62296462059),
+ (0.12701052427, -0.040979906917, 1.1229894161, 0.29097992182),
+ (0.29392692447, -0.20607307553, 0.95607304573, 0.45607307553),
+ (0.33749997616, 0.087500035763, 0.91250002384, 0.66249996424),
+ (0.42170679569, -0.03158634901, 0.82829320431, 0.78158634901),
+ (0.21841356158, 0.17170682549, 1.0315864086, 0.57829320431),
+ (0.45901179314, -0.12296459079, 0.79098820686, 0.87296462059),
+ (0.12701052427, 0.20902009308, 1.1229894161, 0.54097992182),
+ (0.29392692447, 0.043926924467, 0.95607304573, 0.70607304573),
+ (0.33749997616, 0.33750003576, 0.91250002384, 0.91249996424),
+ (0.42170679569, 0.21841365099, 0.82829320431, 1.0315864086),
+ (0.21841356158, 0.42170682549, 1.0315864086, 0.82829320431),
+ (0.45901179314, 0.12703540921, 0.79098820686, 1.1229646206),
+ (0.12701052427, 0.45902007818, 1.1229894161, 0.79097992182),
+ (0.29392692447, 0.29392692447, 0.95607304573, 0.95607304573),
+ (0.33749997616, 0.58750003576, 0.91250002384, 1.1624999046),
+ (0.42170679569, 0.46841365099, 0.82829320431, 1.2815864086),
+ (0.21841356158, 0.67170679569, 1.0315864086, 1.0782932043),
+ (0.45901179314, 0.37703540921, 0.79098820686, 1.3729646206),
+ (0.12701052427, 0.70902007818, 1.1229894161, 1.0409798622),
+ (0.29392692447, 0.54392695427, 0.95607304573, 1.2060730457),
+ (0.58749997616, -0.16249996424, 1.1625000238, 0.41249996424),
+ (0.67170679569, -0.28158634901, 1.0782932043, 0.53158634901),
+ (0.46841356158, -0.078293174505, 1.2815864086, 0.32829317451),
+ (0.70901179314, -0.37296459079, 1.0409882069, 0.62296462059),
+ (0.37701052427, -0.040979906917, 1.3729894161, 0.29097992182),
+ (0.54392695427, -0.20607307553, 1.2060730457, 0.45607307553),
+ (0.58749997616, 0.087500035763, 1.1625000238, 0.66249996424),
+ (0.67170679569, -0.03158634901, 1.0782932043, 0.78158634901),
+ (0.46841356158, 0.17170682549, 1.2815864086, 0.57829320431),
+ (0.70901179314, -0.12296459079, 1.0409882069, 0.87296462059),
+ (0.37701052427, 0.20902009308, 1.3729894161, 0.54097992182),
+ (0.54392695427, 0.043926924467, 1.2060730457, 0.70607304573),
+ (0.58749997616, 0.33750003576, 1.1625000238, 0.91249996424),
+ (0.67170679569, 0.21841365099, 1.0782932043, 1.0315864086),
+ (0.46841356158, 0.42170682549, 1.2815864086, 0.82829320431),
+ (0.70901179314, 0.12703540921, 1.0409882069, 1.1229646206),
+ (0.37701052427, 0.45902007818, 1.3729894161, 0.79097992182),
+ (0.54392695427, 0.29392692447, 1.2060730457, 0.95607304573),
+ (0.58749997616, 0.58750003576, 1.1625000238, 1.1624999046),
+ (0.67170679569, 0.46841365099, 1.0782932043, 1.2815864086),
+ (0.46841356158, 0.67170679569, 1.2815864086, 1.0782932043),
+ (0.70901179314, 0.37703540921, 1.0409882069, 1.3729646206),
+ (0.37701052427, 0.70902007818, 1.3729894161, 1.0409798622),
+ (0.54392695427, 0.54392695427, 1.2060730457, 1.2060730457),
+ (-0.13125002384, -0.13124996424, 0.63125002384, 0.63124996424),
+ (-0.019584476948, -0.28916883469, 0.51958447695, 0.78916883469),
+ (-0.2891689539, -0.019584417343, 0.7891689539, 0.51958441734),
+ (0.029885202646, -0.41034436226, 0.47011479735, 0.91034436226),
+ (-0.41037738323, 0.029896214604, 0.91037738323, 0.4701038003),
+ (-0.17555111647, -0.17555111647, 0.67555111647, 0.67555111647),
+ (-0.13125002384, 0.36875003576, 0.63125002384, 1.1312499046),
+ (-0.019584476948, 0.21083116531, 0.51958447695, 1.2891688347),
+ (-0.2891689539, 0.48041558266, 0.7891689539, 1.0195844173),
+ (0.029885202646, 0.089655637741, 0.47011479735, 1.4103443623),
+ (-0.41037738323, 0.5298961997, 0.91037738323, 0.9701038003),
+ (-0.17555111647, 0.32444888353, 0.67555111647, 1.1755511761),
+ (0.36874997616, -0.13124996424, 1.1312500238, 0.63124996424),
+ (0.48041552305, -0.28916883469, 1.0195844173, 0.78916883469),
+ (0.2108310461, -0.019584417343, 1.2891689539, 0.51958441734),
+ (0.52988517284, -0.41034436226, 0.97011482716, 0.91034436226),
+ (0.089622616768, 0.029896214604, 1.4103773832, 0.4701038003),
+ (0.32444888353, -0.17555111647, 1.1755511761, 0.67555111647),
+ (0.36874997616, 0.36875003576, 1.1312500238, 1.1312499046),
+ (0.48041552305, 0.21083116531, 1.0195844173, 1.2891688347),
+ (0.2108310461, 0.48041558266, 1.2891689539, 1.0195844173),
+ (0.52988517284, 0.089655637741, 0.97011482716, 1.4103443623),
+ (0.089622616768, 0.5298961997, 1.4103773832, 0.9701038003),
+ (0.32444888353, 0.32444888353, 1.1755511761, 1.1755511761),
+ (0.024999976158, 0.025000035763, 0.97500002384, 0.97499996424),
+ (0.16412425041, -0.17175137997, 0.83587574959, 1.17175138),
+ (-0.17175149918, 0.16412431002, 1.1717514992, 0.83587568998),
+ (0.22575861216, -0.32272410393, 0.77424138784, 1.3227241039),
+ (-0.32276523113, 0.22577232122, 1.3227652311, 0.77422767878),
+ (0.012660294771, 0.012660294771, 0.98733973503, 0.98733973503),
+)
diff --git a/src/aiy/vision/models/utils.py b/src/aiy/vision/models/utils.py
new file mode 100644
index 00000000..b83863ec
--- /dev/null
+++ b/src/aiy/vision/models/utils.py
@@ -0,0 +1,9 @@
+"""Utility to load compute graphs from diffrent sources."""
+
+import os
+
+def load_compute_graph(name):
+ path = os.path.join('/opt/aiy/models', name)
+ with open(path, 'rb') as f:
+ return f.read()
+
diff --git a/src/aiy/vision/pins.py b/src/aiy/vision/pins.py
new file mode 100644
index 00000000..ee580673
--- /dev/null
+++ b/src/aiy/vision/pins.py
@@ -0,0 +1,644 @@
+"""Pin definitions for use with gpiozero devices.
+
+Defines pin objects, and overrides the pin factory used by gpiozero devices to
+support the pins routed through the hat. Should not disrupt usage of other pins.
+"""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from collections import namedtuple
+from copy import deepcopy
+from os import listdir
+import time
+from gpiozero import Device
+from gpiozero import Factory
+from gpiozero import Pin
+from gpiozero.exc import GPIOPinInUse
+from gpiozero.exc import InputDeviceError
+from gpiozero.exc import PinFixedPull
+from gpiozero.exc import PinInvalidBounce
+from gpiozero.exc import PinInvalidEdges
+from gpiozero.exc import PinPWMUnsupported
+from gpiozero.exc import PinSetInput
+from gpiozero.exc import PinUnsupported
+from gpiozero.threads import GPIOThread
+
+
+def _detect_gpio_offset(module_path):
+ for folder in listdir(module_path):
+ try:
+ with open('%s/%s/base' % (module_path, folder), 'r') as offset:
+ return int(offset.read())
+ except IOError:
+ pass
+ return None
+
+
+_FsNodeSpec = namedtuple('_FsNodeSpec', ['pin', 'name'])
+
+
+class GpioSpec(_FsNodeSpec):
+ _MODULE_PATH = '/sys/bus/i2c/drivers/aiy-io-i2c/1-0051/gpio-aiy-io/gpio'
+ _PIN_OFFSET = _detect_gpio_offset(_MODULE_PATH)
+
+ def __new__(cls, pin, name):
+ return super(GpioSpec, cls).__new__(cls, GpioSpec._PIN_OFFSET + pin, name)
+
+ def __str__(self):
+ return 'gpio %s (%d)' % (self.name, self.pin - self._PIN_OFFSET)
+
+
+class PwmSpec(_FsNodeSpec):
+
+ def __str__(self):
+ return 'pwm %d' % self.pin
+
+
+AIYPinSpec = namedtuple('AIYPinSpec', ['gpio_spec', 'pwm_spec'])
+
+PIN_A = AIYPinSpec(GpioSpec(2, 'AIY_USER0'), PwmSpec(0, 'pwm0'))
+PIN_B = AIYPinSpec(GpioSpec(3, 'AIY_USER1'), PwmSpec(1, 'pwm1'))
+PIN_C = AIYPinSpec(GpioSpec(8, 'AIY_USER2'), PwmSpec(2, 'pwm2'))
+PIN_D = AIYPinSpec(GpioSpec(9, 'AIY_USER3'), PwmSpec(3, 'pwm3'))
+LED_1 = AIYPinSpec(GpioSpec(13, 'AIY_LED0'), None)
+LED_2 = AIYPinSpec(GpioSpec(14, 'AIY_LED1'), None)
+
+BUZZER_GPIO_PIN = 22
+BUTTON_GPIO_PIN = 23
+
+_NS_PER_SECOND = 1000000000
+
+
+class SysFsPin(object):
+ """Generic SysFsPin which implements generic SysFs driver functionality."""
+
+ def __init__(self, spec, fs_root):
+ self._pin = spec.pin
+ self._name = spec.name
+ self._fs_root = fs_root
+ # Ensure things start out unexported.
+ try:
+ self.unexport()
+ except IOError:
+ pass
+
+ def set_function(self, function):
+ raise NotImplementedError('Setting function not supported')
+
+ def get_function(self):
+ raise NotImplementedError('Getting function not supported')
+
+ def export(self):
+ try:
+ with open(self.root_path('export'), 'w') as export:
+ export.write('%d' % self._pin)
+ except IOError:
+ raise GPIOPinInUse('Pin already in use')
+
+ def unexport(self):
+ with open(self.root_path('unexport'), 'w') as unexport:
+ unexport.write('%d' % self._pin)
+
+ def open(self):
+ self.export()
+
+ def close(self):
+ self.unexport()
+
+ def wait_for_permissions(self, prop):
+ """Wait for write permissions on the given property.
+
+ We must wait because the the file system needs to grant permissions for the
+ newly created node."""
+ while True:
+ try:
+ with open(self.property_path(prop), 'w'):
+ pass
+ return
+ except IOError:
+ time.sleep(.01)
+
+ def get_value(self):
+ raise NotImplementedError('Value getting not implemented')
+
+ def set_value(self, value):
+ raise NotImplementedError('Value setting not implemented')
+
+ def write_property(self, prop, value):
+ """Writes the given sysfs node property to the pin."""
+ with open(self.property_path(prop), 'w') as node:
+ node.write(value)
+
+ def read_property(self, prop):
+ """Reads the given sysfs node property from the pin."""
+ with open(self.property_path(prop), 'r') as node:
+ return node.read()
+
+ def root_path(self, node):
+ return '%s/%s' % (self._fs_root, node)
+
+ def property_path(self, prop):
+ return '%s/%s/%s' % (self._fs_root, self._name, prop)
+
+
+class SysFsGpioPin(SysFsPin):
+ """SysFs support for GPIO pins.
+
+ Supports the SysFs node for GPIO control.
+ """
+ _FS_ROOT = '/sys/class/gpio'
+
+ def __init__(self, spec):
+ super(SysFsGpioPin, self).__init__(spec, self._FS_ROOT)
+ if not isinstance(spec, GpioSpec):
+ raise TypeError('Pin specification not compatible with SysFS GPIO')
+ self._spec = spec
+ self._out = False
+ self._value = None
+
+ def _get_direction(self):
+ return self.read_property('direction')
+
+ def _set_direction(self, direction):
+ if direction not in ('in', 'out'):
+ raise ValueError('Direction must be either in or out')
+ self.write_property('direction', direction)
+
+ def _get_value(self):
+ return self.read_property('value')
+
+ def _set_value(self, value):
+ self.write_property('value', value)
+
+ def _get_active_low(self):
+ return self.read_property('active_low')
+
+ def _set_active_low(self, active_low):
+ self.write_property('active_low', '1' if active_low else '0')
+
+ def set_function(self, function):
+ if function == 'input':
+ self._set_direction('in')
+ self._out = False
+ elif function == 'output':
+ self._set_direction('out')
+ self._out = True
+ else:
+ raise ValueError('pin function must be either input or output')
+
+ def get_function(self):
+ direction = self._get_direction()
+ if direction == 'input':
+ return 'in'
+ if direction == 'output':
+ return 'out'
+
+ def set_value(self, value):
+ if not self._out:
+ raise PinSetInput('Pin is not open for output')
+ self._set_value('1' if value else '0')
+ self._value = value
+
+ def get_value(self):
+ if self._out:
+ return self._value
+ return bool(int(self._get_value()))
+
+ def open(self):
+ super(SysFsGpioPin, self).open()
+ self.wait_for_permissions('active_low')
+ self.wait_for_permissions('direction')
+ # GPIO pins on the hat seem to be inverted by default.
+ self._set_active_low(True)
+
+ def close(self):
+ # Restore the default direction (turns off LED) before closing.
+ self._set_direction('in')
+ super(SysFsGpioPin, self).close()
+
+
+class SysFsPwmPin(SysFsPin):
+ """SysFs support for PWM pins.
+
+ Supports the SysFs node for pwm control.
+ """
+ _FS_ROOT = '/sys/class/pwm/pwmchip0'
+
+ class PwmState(object):
+ """Container for the state of the pwm.
+
+ Used to recover after disable/enable and ensure consistency.
+ """
+
+ def __init__(self):
+ self.duty_cycle = 0
+ self.period_ns = _NS_PER_SECOND / 50
+ self.enabled = False
+ self.function = None
+
+ def __init__(self, spec):
+ super(SysFsPwmPin, self).__init__(spec, self._FS_ROOT)
+ if not isinstance(spec, PwmSpec):
+ raise TypeError('Pin specification not compatible with SysFS PWM')
+ if spec.pin < 0 or spec.pin > 3:
+ raise ValueError('Pin must be between 0 and 3 (inclusive)')
+ self._spec = spec
+ self._state = SysFsPwmPin.PwmState()
+
+ def _set_enabled(self, enabled):
+ self.write_property('enable', '1' if enabled else '0')
+ self._state.enabled = enabled
+
+ def _get_enabled(self):
+ return int(self.read_property('enable')) != 0
+
+ def _set_period_ns(self, period_ns):
+ self.write_property('period', '%d' % period_ns)
+ self._state.period_ns = int(period_ns)
+
+ def _get_period_ns(self):
+ return int(self.read_property('period'))
+
+ def _set_duty_cycle(self, duty_cycle):
+ self.write_property('duty_cycle', '%d' % duty_cycle)
+ self._state.duty_cycle = duty_cycle
+
+ def _get_duty_cycle(self):
+ return int(self.read_property('duty_cycle'))
+
+ def _update_state(self, new_state):
+ # Each time we enable, we need to first re-set the period and duty cycle (in
+ # that order).
+ if new_state.period_ns != self._state.period_ns or (not self._state.enabled
+ and new_state.enabled):
+ self._set_period_ns(new_state.period_ns)
+ if new_state.duty_cycle != self._state.duty_cycle or (
+ not self._state.enabled and new_state.enabled):
+ self._set_duty_cycle(new_state.duty_cycle)
+ if new_state.enabled != self._state.enabled:
+ self._set_enabled(new_state.enabled)
+
+ def _read_state(self):
+ self._state.period_ns = self._get_period_ns()
+ self._state.enabled = self._get_enabled()
+ self._state.duty_cycle = self._get_duty_cycle()
+
+ def set_function(self, function):
+ if function != 'pwm' and function != 'output':
+ raise ValueError('PWM pins only support pwm and output functionality')
+ self._state.function = function
+
+ def get_function(self):
+ return self._state.function
+
+ def get_value(self):
+ return self._state.duty_cycle / self._state.period_ns
+
+ def set_value(self, value):
+ new_state = deepcopy(self._state)
+ if value is None:
+ new_state.enabled = False
+ else:
+ new_state.enabled = True
+ new_state.duty_cycle = value * self._state.period_ns
+ self._update_state(new_state)
+
+ def set_period_ns(self, period_ns):
+ new_state = deepcopy(self._state)
+ new_state.period_ns = period_ns
+ self._update_state(new_state)
+
+ def get_period_ns(self):
+ return self._state.period_ns
+
+ def open(self):
+ super(SysFsPwmPin, self).open()
+ self.wait_for_permissions('period')
+ self.wait_for_permissions('enable')
+ self._read_state()
+ new_state = deepcopy(self._state)
+ new_state.period_ns = _NS_PER_SECOND / 50
+ new_state.enabled = True
+ self._update_state(new_state)
+
+ def close(self):
+ self._set_enabled(False)
+ super(SysFsPwmPin, self).close()
+
+
+# Debounce by making sure the last change wasn't less than d_time in the past ->
+# should be agnostic to direction.
+class DebouncingPoller(object):
+ """Manages debouncing and polling a function periodically in the background.
+
+ Calls a given getter periodically and when the debounced value changes such
+ that detector(old, new) returns true, the callback is called. Only runs while
+ detector, getter, and callback are set.
+ """
+ _MIN_POLL_INTERVAL = .0001
+
+ def __init__(self, value_getter, callback, detector=lambda old, new: True):
+ self._poll_thread = None
+ self._debounce_time = .001
+ self._poll_interval = .00051
+ self._getter = value_getter
+ self._detector = detector
+ self._callback = callback
+
+ @property
+ def poll_interval(self):
+ return self._poll_interval
+
+ @poll_interval.setter
+ def poll_interval(self, interval):
+ self._poll_interval = max(interval, self._MIN_POLL_INTERVAL)
+ self.restart_polling()
+
+ @property
+ def debounce_time(self):
+ return self._debounce_time
+
+ @debounce_time.setter
+ def debounce_time(self, debounce_time):
+ self._debounce_time = debounce_time
+ self.restart_polling()
+
+ @property
+ def callback(self):
+ return self._callback
+
+ @callback.setter
+ def callback(self, callback):
+ self.stop_polling()
+ self._callback = callback
+ self.try_start_polling()
+
+ @property
+ def detector(self):
+ return self._detector
+
+ @detector.setter
+ def detector(self, detector):
+ self._detector = detector
+ self.restart_polling()
+
+ def try_start_polling(self):
+ if (not self._poll_thread and self._getter and self._callback and
+ self._detector):
+ self._poll_thread = GPIOThread(
+ target=self._poll,
+ args=(self._poll_interval, self._debounce_time, self._getter,
+ self._detector, self._callback))
+ self._poll_thread.start()
+
+ def stop_polling(self):
+ if self._poll_thread:
+ self._poll_thread.stop()
+ self._poll_thread = None
+
+ def restart_polling(self):
+ self.stop_polling()
+ self.try_start_polling()
+
+ # Only called from the polling thread.
+ def _poll(self, poll_interval, debounce_interval, getter, detector, callback):
+ """Debounces and monitors the value retrieved by _getter.
+
+ Triggers callback if detector(old_value, new_value) returns true.
+ Args:
+ poll_interval: positive float, time in seconds between polling the getter.
+ debounce_interval: positive float, time in seconds to wait after a change
+ to allow a future change to the value to trigger the callback.
+ getter: function() -> value, gets the value. This will be called
+ periodically and the value type will be the same type passed to the
+ detector function.
+ detector: function(old, new) -> bool, filters changes to determine when
+ the callback should be called. Can be used for edge detection
+ callback: function() to be invoked when detector conditions are met.
+ """
+ last_time = time.time()
+ last_value = getter()
+ while not self._poll_thread.stopping.wait(poll_interval):
+ value = getter()
+ new_time = time.time()
+ if not debounce_interval or (new_time - last_time) > debounce_interval:
+ if detector(last_value, value):
+ callback()
+ last_value = value
+ last_time = new_time
+
+
+class HatPin(Pin):
+ """A Pin implemenation that supports pins controlled by the hat's MCU.
+
+ Only one HatPin should exist at a given time for a given pin system wide.
+ Behavior is completely unpredictable if more than one pin exists concurrently.
+ If the factory is used for construction there are protections in place to
+ prevent this, however if multiple programs are running simultaneously the
+ protections do not limit cross program duplication.
+ """
+ _EDGE_DETECTORS = {
+ 'both': lambda old, new: old != new,
+ 'rising': lambda old, new: not old and new,
+ 'falling': lambda old, new: old and not new,
+ None: None,
+ }
+
+ def __init__(self, spec, pwm=False):
+ super(HatPin, self).__init__()
+ self.gpio_pin = None
+ self.pwm_pin = None
+ self.pwm_active = False
+ self.gpio_active = False
+ if spec.gpio_spec is not None:
+ self.gpio_pin = SysFsGpioPin(spec.gpio_spec)
+
+ if spec.pwm_spec is not None:
+ self.pwm_pin = SysFsPwmPin(spec.pwm_spec)
+
+ self._closed = False
+ self._poller = DebouncingPoller(self._get_state, None)
+ self._edges = None
+ self._set_bounce(.001)
+ # Start out with gpio enabled for compatibility.
+ self._enable_gpio()
+
+ def _enable_pwm(self):
+ if self._closed:
+ return
+ if self.pwm_pin is None:
+ raise PinPWMUnsupported(
+ 'PWM was enabled, but is not supported on pin %r' % self.pwm_pin)
+ self._disable_gpio()
+ if not self.pwm_active:
+ self.pwm_pin.open()
+ self.pwm_active = True
+
+ def _disable_pwm(self):
+ if self.pwm_active and self.pwm_pin is not None:
+ self.pwm_pin.close()
+ self.pwm_active = False
+
+ def _enable_gpio(self):
+ if self._closed:
+ return
+ if self.gpio_pin is None:
+ raise PinUnsupported(
+ 'GPIO was enabled, but is not supported on pin %r' % self.gpio_pin)
+ self._disable_pwm()
+ if not self.gpio_active:
+ self.gpio_pin.open()
+ self.gpio_active = True
+
+ def _disable_gpio(self):
+ if self.gpio_active and self.gpio_pin is not None:
+ self.gpio_pin.close()
+ self.gpio_active = False
+
+ def close(self):
+ self._closed = True
+ self._poller.stop_polling()
+ self._disable_pwm()
+ self._disable_gpio()
+
+ def _active_pin(self):
+ if self.pwm_active:
+ return self.pwm_pin
+ if self.gpio_active:
+ return self.gpio_pin
+ return None
+
+ def _get_function(self):
+ return self._active_pin().get_function()
+
+ def _set_function(self, value):
+ if value == 'input':
+ if self.pwm_active:
+ raise InputDeviceError('PWM Pin cannot be set to input')
+ self._enable_gpio()
+ elif value == 'pwm':
+ if self.gpio_active:
+ raise PinPWMUnsupported('GPIO Pin cannot be set to pwm')
+ self._enable_pwm()
+ elif self._active_pin() is None:
+ self._enable_gpio()
+
+ if value != 'input':
+ self._poller.stop_polling()
+ self._active_pin().set_function(value)
+
+ def _get_state(self):
+ return self._active_pin().get_value()
+
+ def _set_state(self, state):
+ self._active_pin().set_value(state)
+
+ def _get_frequency(self):
+ if self.pwm_pin is None or not self.pwm_active:
+ return None
+ return _NS_PER_SECOND / self.pwm_pin.get_period_ns()
+
+ def _set_frequency(self, frequency):
+ if frequency is None:
+ self._enable_gpio()
+ else:
+ self._enable_pwm()
+ self.pwm_pin.set_period_ns(_NS_PER_SECOND / frequency)
+
+ def _set_pull(self, pull):
+ if pull != 'up':
+ raise PinFixedPull('Only pull up is supported right now (%s)' % pull)
+
+ def _get_pull(self):
+ return 'up'
+
+ def _set_edges(self, edges):
+ if edges not in HatPin._EDGE_DETECTORS.keys():
+ raise PinInvalidEdges('Edge must be "both", "falling", "rising", or None')
+ self._poller.detector = HatPin._EDGE_DETECTORS[edges]
+ self._edges = edges
+
+ def _get_edges(self):
+ return self._edges
+
+ def _set_when_changed(self, callback):
+ self._poller.callback = callback
+
+ def _get_when_changed(self):
+ return self._poller.callback
+
+ def set_poll_interval(self, poll_interval):
+ """Sets the time between polling the pin value.
+
+ If a debounce time is set, this will be set to .51 * the debounce time.
+ There is a natural minimum value of _MIN_POLL_INTERVAL to which all smaller
+ values will be clipped.
+ Args:
+ poll_interval: positve float, time in seconds between polling the pin.
+ """
+ self._poller.poll_interval = poll_interval
+
+ def _set_bounce(self, debounce_time):
+ if debounce_time is None:
+ self._poller.debounce_time = debounce_time
+ elif debounce_time < 0:
+ raise PinInvalidBounce('Bounce must be positive.')
+ else:
+ self._poller.debounce_time = debounce_time
+ self.set_poll_interval(debounce_time * .51)
+
+ def _get_bounce(self):
+ return self._poller.debounce_time
+
+
+class HybridFactory(Factory):
+ """Factory for selecting between other factories based on priority/success."""
+
+ def __init__(self, *factories):
+ super(HybridFactory, self).__init__()
+ self.factories = factories
+
+ def close(self):
+ for factory in self.factories:
+ factory.close()
+
+ def pin(self, spec):
+ for factory in self.factories:
+ try:
+ # Try to make the pin from each factory (in order), until one works.
+ return factory.pin(spec)
+ except (TypeError, ValueError):
+ pass
+ raise TypeError(
+ 'No registered factory was able to construct a pin for the given '
+ 'specification')
+
+
+class HatFactory(Factory):
+ """Factory for pins accessed through the hat's MCU."""
+ pins = {}
+
+ def __init__(self):
+ super(HatFactory, self).__init__()
+
+ self.pins = HatFactory.pins
+
+ def close(self):
+ for pin in self.pins.values():
+ pin.close()
+
+ def pin(self, spec):
+ if spec in self.pins:
+ return self.pins.get(spec)
+ if isinstance(spec, AIYPinSpec):
+ pin = HatPin(spec)
+ self.pins[spec] = pin
+ return pin
+ raise TypeError('Hat factory invoked on non-hat pin')
+
+
+# This overrides the default factory being used by all gpiozero devices. It will
+# defer to the previous default for all non-hat pins.
+hat_factory = HatFactory()
+Device.pin_factory = HybridFactory(hat_factory, Device.pin_factory)
diff --git a/src/aiy/vision/proto/__init__.py b/src/aiy/vision/proto/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/aiy/vision/proto/protocol_pb2.py b/src/aiy/vision/proto/protocol_pb2.py
new file mode 100644
index 00000000..b9bd4837
--- /dev/null
+++ b/src/aiy/vision/proto/protocol_pb2.py
@@ -0,0 +1,1115 @@
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# source: protocol.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf import descriptor_pb2
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+ name='protocol.proto',
+ package='spacepark.vision',
+ syntax='proto3',
+ serialized_pb=_b('\n\x0eprotocol.proto\x12\x10spacepark.vision\"@\n\tRectangle\x12\t\n\x01x\x18\x01 \x01(\x05\x12\t\n\x01y\x18\x02 \x01(\x05\x12\r\n\x05width\x18\x03 \x01(\x05\x12\x0e\n\x06height\x18\x04 \x01(\x05\"J\n\x0bTensorShape\x12\r\n\x05\x62\x61tch\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\r\n\x05width\x18\x03 \x01(\x05\x12\r\n\x05\x64\x65pth\x18\x04 \x01(\x05\"H\n\nByteTensor\x12,\n\x05shape\x18\x01 \x01(\x0b\x32\x1d.spacepark.vision.TensorShape\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"I\n\x0b\x46loatTensor\x12,\n\x05shape\x18\x01 \x01(\x0b\x32\x1d.spacepark.vision.TensorShape\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x02\"0\n\x10TensorNormalizer\x12\x0c\n\x04mean\x18\x01 \x01(\x02\x12\x0e\n\x06stddev\x18\x02 \x01(\x02\"\xa2\t\n\x07Request\x12\x39\n\nload_model\x18\x01 \x01(\x0b\x32#.spacepark.vision.Request.LoadModelH\x00\x12=\n\x0cunload_model\x18\x02 \x01(\x0b\x32%.spacepark.vision.Request.UnloadModelH\x00\x12\x43\n\x0fimage_inference\x18\x03 \x01(\x0b\x32(.spacepark.vision.Request.ImageInferenceH\x00\x12P\n\x16start_camera_inference\x18\x04 \x01(\x0b\x32..spacepark.vision.Request.StartCameraInferenceH\x00\x12\x45\n\x10\x63\x61mera_inference\x18\x05 \x01(\x0b\x32).spacepark.vision.Request.CameraInferenceH\x00\x12N\n\x15stop_camera_inference\x18\x06 \x01(\x0b\x32-.spacepark.vision.Request.StopCameraInferenceH\x00\x12>\n\rimu_self_test\x18\x07 \x01(\x0b\x32%.spacepark.vision.Request.ImuSelfTestH\x00\x1a\xa8\x01\n\tLoadModel\x12\x12\n\nmodel_name\x18\x01 \x01(\t\x12\x32\n\x0binput_shape\x18\x02 \x01(\x0b\x32\x1d.spacepark.vision.TensorShape\x12<\n\x10input_normalizer\x18\x03 \x01(\x0b\x32\".spacepark.vision.TensorNormalizer\x12\x15\n\rcompute_graph\x18\x04 \x01(\x0c\x1a!\n\x0bUnloadModel\x12\x12\n\nmodel_name\x18\x01 \x01(\t\x1a\xc7\x01\n\x0eImageInference\x12\x12\n\nmodel_name\x18\x01 \x01(\t\x12,\n\x06tensor\x18\x02 \x01(\x0b\x32\x1c.spacepark.vision.ByteTensor\x12\x44\n\x06params\x18\x03 \x03(\x0b\x32\x34.spacepark.vision.Request.ImageInference.ParamsEntry\x1a-\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\xd2\x01\n\x14StartCameraInference\x12\x12\n\nmodel_name\x18\x01 \x01(\t\x12+\n\x06window\x18\x02 \x01(\x0b\x32\x1b.spacepark.vision.Rectangle\x12J\n\x06params\x18\x03 \x03(\x0b\x32:.spacepark.vision.Request.StartCameraInference.ParamsEntry\x1a-\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x11\n\x0f\x43\x61meraInference\x1a\x15\n\x13StopCameraInference\x1a\r\n\x0bImuSelfTestB\t\n\x07request\"\xa9\x03\n\x0fInferenceResult\x12\x12\n\nmodel_name\x18\x01 \x01(\t\x12\r\n\x05width\x18\x02 \x01(\x05\x12\x0e\n\x06height\x18\x03 \x01(\x05\x12+\n\x06window\x18\x04 \x01(\x0b\x32\x1b.spacepark.vision.Rectangle\x12\x13\n\x0b\x64uration_ms\x18\x05 \x01(\x05\x12?\n\x07tensors\x18\x06 \x03(\x0b\x32..spacepark.vision.InferenceResult.TensorsEntry\x12\x36\n\x05\x66rame\x18\x07 \x01(\x0b\x32\'.spacepark.vision.InferenceResult.Frame\x1aM\n\x0cTensorsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12,\n\x05value\x18\x02 \x01(\x0b\x32\x1d.spacepark.vision.FloatTensor:\x02\x38\x01\x1aY\n\x05\x46rame\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x14\n\x0ctimestamp_us\x18\x02 \x01(\x03\x12+\n\x05image\x18\x03 \x01(\x0b\x32\x1c.spacepark.vision.ByteTensor\"\xe4\x01\n\x08Response\x12\x30\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .spacepark.vision.Response.Error\x12\x31\n\x06result\x18\x02 \x01(\x0b\x32!.spacepark.vision.InferenceResult\x1as\n\x05\x45rror\x12\x33\n\x04type\x18\x01 \x01(\x0e\x32%.spacepark.vision.Response.Error.Type\x12\x0b\n\x03msg\x18\x02 \x01(\t\"(\n\x04Type\x12\x08\n\x04INFO\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x62\x06proto3')
+)
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+
+
+_RESPONSE_ERROR_TYPE = _descriptor.EnumDescriptor(
+ name='Type',
+ full_name='spacepark.vision.Response.Error.Type',
+ filename=None,
+ file=DESCRIPTOR,
+ values=[
+ _descriptor.EnumValueDescriptor(
+ name='INFO', index=0, number=0,
+ options=None,
+ type=None),
+ _descriptor.EnumValueDescriptor(
+ name='WARNING', index=1, number=1,
+ options=None,
+ type=None),
+ _descriptor.EnumValueDescriptor(
+ name='ERROR', index=2, number=2,
+ options=None,
+ type=None),
+ ],
+ containing_type=None,
+ options=None,
+ serialized_start=2183,
+ serialized_end=2223,
+)
+_sym_db.RegisterEnumDescriptor(_RESPONSE_ERROR_TYPE)
+
+
+_RECTANGLE = _descriptor.Descriptor(
+ name='Rectangle',
+ full_name='spacepark.vision.Rectangle',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='x', full_name='spacepark.vision.Rectangle.x', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='y', full_name='spacepark.vision.Rectangle.y', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='width', full_name='spacepark.vision.Rectangle.width', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='height', full_name='spacepark.vision.Rectangle.height', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=36,
+ serialized_end=100,
+)
+
+
+_TENSORSHAPE = _descriptor.Descriptor(
+ name='TensorShape',
+ full_name='spacepark.vision.TensorShape',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='batch', full_name='spacepark.vision.TensorShape.batch', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='height', full_name='spacepark.vision.TensorShape.height', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='width', full_name='spacepark.vision.TensorShape.width', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='depth', full_name='spacepark.vision.TensorShape.depth', index=3,
+ number=4, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=102,
+ serialized_end=176,
+)
+
+
+_BYTETENSOR = _descriptor.Descriptor(
+ name='ByteTensor',
+ full_name='spacepark.vision.ByteTensor',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='shape', full_name='spacepark.vision.ByteTensor.shape', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='data', full_name='spacepark.vision.ByteTensor.data', index=1,
+ number=2, type=12, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b(""),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=178,
+ serialized_end=250,
+)
+
+
+_FLOATTENSOR = _descriptor.Descriptor(
+ name='FloatTensor',
+ full_name='spacepark.vision.FloatTensor',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='shape', full_name='spacepark.vision.FloatTensor.shape', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='data', full_name='spacepark.vision.FloatTensor.data', index=1,
+ number=2, type=2, cpp_type=6, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=252,
+ serialized_end=325,
+)
+
+
+_TENSORNORMALIZER = _descriptor.Descriptor(
+ name='TensorNormalizer',
+ full_name='spacepark.vision.TensorNormalizer',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='mean', full_name='spacepark.vision.TensorNormalizer.mean', index=0,
+ number=1, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='stddev', full_name='spacepark.vision.TensorNormalizer.stddev', index=1,
+ number=2, type=2, cpp_type=6, label=1,
+ has_default_value=False, default_value=float(0),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=327,
+ serialized_end=375,
+)
+
+
+_REQUEST_LOADMODEL = _descriptor.Descriptor(
+ name='LoadModel',
+ full_name='spacepark.vision.Request.LoadModel',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='model_name', full_name='spacepark.vision.Request.LoadModel.model_name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='input_shape', full_name='spacepark.vision.Request.LoadModel.input_shape', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='input_normalizer', full_name='spacepark.vision.Request.LoadModel.input_normalizer', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='compute_graph', full_name='spacepark.vision.Request.LoadModel.compute_graph', index=3,
+ number=4, type=12, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b(""),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=878,
+ serialized_end=1046,
+)
+
+_REQUEST_UNLOADMODEL = _descriptor.Descriptor(
+ name='UnloadModel',
+ full_name='spacepark.vision.Request.UnloadModel',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='model_name', full_name='spacepark.vision.Request.UnloadModel.model_name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1048,
+ serialized_end=1081,
+)
+
+_REQUEST_IMAGEINFERENCE_PARAMSENTRY = _descriptor.Descriptor(
+ name='ParamsEntry',
+ full_name='spacepark.vision.Request.ImageInference.ParamsEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='key', full_name='spacepark.vision.Request.ImageInference.ParamsEntry.key', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='value', full_name='spacepark.vision.Request.ImageInference.ParamsEntry.value', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')),
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1238,
+ serialized_end=1283,
+)
+
+_REQUEST_IMAGEINFERENCE = _descriptor.Descriptor(
+ name='ImageInference',
+ full_name='spacepark.vision.Request.ImageInference',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='model_name', full_name='spacepark.vision.Request.ImageInference.model_name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='tensor', full_name='spacepark.vision.Request.ImageInference.tensor', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='params', full_name='spacepark.vision.Request.ImageInference.params', index=2,
+ number=3, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[_REQUEST_IMAGEINFERENCE_PARAMSENTRY, ],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1084,
+ serialized_end=1283,
+)
+
+_REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY = _descriptor.Descriptor(
+ name='ParamsEntry',
+ full_name='spacepark.vision.Request.StartCameraInference.ParamsEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='key', full_name='spacepark.vision.Request.StartCameraInference.ParamsEntry.key', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='value', full_name='spacepark.vision.Request.StartCameraInference.ParamsEntry.value', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')),
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1238,
+ serialized_end=1283,
+)
+
+_REQUEST_STARTCAMERAINFERENCE = _descriptor.Descriptor(
+ name='StartCameraInference',
+ full_name='spacepark.vision.Request.StartCameraInference',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='model_name', full_name='spacepark.vision.Request.StartCameraInference.model_name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='window', full_name='spacepark.vision.Request.StartCameraInference.window', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='params', full_name='spacepark.vision.Request.StartCameraInference.params', index=2,
+ number=3, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[_REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY, ],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1286,
+ serialized_end=1496,
+)
+
+_REQUEST_CAMERAINFERENCE = _descriptor.Descriptor(
+ name='CameraInference',
+ full_name='spacepark.vision.Request.CameraInference',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1498,
+ serialized_end=1515,
+)
+
+_REQUEST_STOPCAMERAINFERENCE = _descriptor.Descriptor(
+ name='StopCameraInference',
+ full_name='spacepark.vision.Request.StopCameraInference',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1517,
+ serialized_end=1538,
+)
+
+_REQUEST_IMUSELFTEST = _descriptor.Descriptor(
+ name='ImuSelfTest',
+ full_name='spacepark.vision.Request.ImuSelfTest',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1540,
+ serialized_end=1553,
+)
+
+_REQUEST = _descriptor.Descriptor(
+ name='Request',
+ full_name='spacepark.vision.Request',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='load_model', full_name='spacepark.vision.Request.load_model', index=0,
+ number=1, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='unload_model', full_name='spacepark.vision.Request.unload_model', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='image_inference', full_name='spacepark.vision.Request.image_inference', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='start_camera_inference', full_name='spacepark.vision.Request.start_camera_inference', index=3,
+ number=4, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='camera_inference', full_name='spacepark.vision.Request.camera_inference', index=4,
+ number=5, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='stop_camera_inference', full_name='spacepark.vision.Request.stop_camera_inference', index=5,
+ number=6, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='imu_self_test', full_name='spacepark.vision.Request.imu_self_test', index=6,
+ number=7, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[_REQUEST_LOADMODEL, _REQUEST_UNLOADMODEL, _REQUEST_IMAGEINFERENCE, _REQUEST_STARTCAMERAINFERENCE, _REQUEST_CAMERAINFERENCE, _REQUEST_STOPCAMERAINFERENCE, _REQUEST_IMUSELFTEST, ],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ _descriptor.OneofDescriptor(
+ name='request', full_name='spacepark.vision.Request.request',
+ index=0, containing_type=None, fields=[]),
+ ],
+ serialized_start=378,
+ serialized_end=1564,
+)
+
+
+_INFERENCERESULT_TENSORSENTRY = _descriptor.Descriptor(
+ name='TensorsEntry',
+ full_name='spacepark.vision.InferenceResult.TensorsEntry',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='key', full_name='spacepark.vision.InferenceResult.TensorsEntry.key', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='value', full_name='spacepark.vision.InferenceResult.TensorsEntry.value', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=_descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001')),
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1824,
+ serialized_end=1901,
+)
+
+_INFERENCERESULT_FRAME = _descriptor.Descriptor(
+ name='Frame',
+ full_name='spacepark.vision.InferenceResult.Frame',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='index', full_name='spacepark.vision.InferenceResult.Frame.index', index=0,
+ number=1, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='timestamp_us', full_name='spacepark.vision.InferenceResult.Frame.timestamp_us', index=1,
+ number=2, type=3, cpp_type=2, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='image', full_name='spacepark.vision.InferenceResult.Frame.image', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1903,
+ serialized_end=1992,
+)
+
+_INFERENCERESULT = _descriptor.Descriptor(
+ name='InferenceResult',
+ full_name='spacepark.vision.InferenceResult',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='model_name', full_name='spacepark.vision.InferenceResult.model_name', index=0,
+ number=1, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='width', full_name='spacepark.vision.InferenceResult.width', index=1,
+ number=2, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='height', full_name='spacepark.vision.InferenceResult.height', index=2,
+ number=3, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='window', full_name='spacepark.vision.InferenceResult.window', index=3,
+ number=4, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='duration_ms', full_name='spacepark.vision.InferenceResult.duration_ms', index=4,
+ number=5, type=5, cpp_type=1, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='tensors', full_name='spacepark.vision.InferenceResult.tensors', index=5,
+ number=6, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='frame', full_name='spacepark.vision.InferenceResult.frame', index=6,
+ number=7, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[_INFERENCERESULT_TENSORSENTRY, _INFERENCERESULT_FRAME, ],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1567,
+ serialized_end=1992,
+)
+
+
+_RESPONSE_ERROR = _descriptor.Descriptor(
+ name='Error',
+ full_name='spacepark.vision.Response.Error',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='type', full_name='spacepark.vision.Response.Error.type', index=0,
+ number=1, type=14, cpp_type=8, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='msg', full_name='spacepark.vision.Response.Error.msg', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ has_default_value=False, default_value=_b("").decode('utf-8'),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[],
+ enum_types=[
+ _RESPONSE_ERROR_TYPE,
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=2108,
+ serialized_end=2223,
+)
+
+_RESPONSE = _descriptor.Descriptor(
+ name='Response',
+ full_name='spacepark.vision.Response',
+ filename=None,
+ file=DESCRIPTOR,
+ containing_type=None,
+ fields=[
+ _descriptor.FieldDescriptor(
+ name='errors', full_name='spacepark.vision.Response.errors', index=0,
+ number=1, type=11, cpp_type=10, label=3,
+ has_default_value=False, default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ _descriptor.FieldDescriptor(
+ name='result', full_name='spacepark.vision.Response.result', index=1,
+ number=2, type=11, cpp_type=10, label=1,
+ has_default_value=False, default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[_RESPONSE_ERROR, ],
+ enum_types=[
+ ],
+ options=None,
+ is_extendable=False,
+ syntax='proto3',
+ extension_ranges=[],
+ oneofs=[
+ ],
+ serialized_start=1995,
+ serialized_end=2223,
+)
+
+_BYTETENSOR.fields_by_name['shape'].message_type = _TENSORSHAPE
+_FLOATTENSOR.fields_by_name['shape'].message_type = _TENSORSHAPE
+_REQUEST_LOADMODEL.fields_by_name['input_shape'].message_type = _TENSORSHAPE
+_REQUEST_LOADMODEL.fields_by_name['input_normalizer'].message_type = _TENSORNORMALIZER
+_REQUEST_LOADMODEL.containing_type = _REQUEST
+_REQUEST_UNLOADMODEL.containing_type = _REQUEST
+_REQUEST_IMAGEINFERENCE_PARAMSENTRY.containing_type = _REQUEST_IMAGEINFERENCE
+_REQUEST_IMAGEINFERENCE.fields_by_name['tensor'].message_type = _BYTETENSOR
+_REQUEST_IMAGEINFERENCE.fields_by_name['params'].message_type = _REQUEST_IMAGEINFERENCE_PARAMSENTRY
+_REQUEST_IMAGEINFERENCE.containing_type = _REQUEST
+_REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY.containing_type = _REQUEST_STARTCAMERAINFERENCE
+_REQUEST_STARTCAMERAINFERENCE.fields_by_name['window'].message_type = _RECTANGLE
+_REQUEST_STARTCAMERAINFERENCE.fields_by_name['params'].message_type = _REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY
+_REQUEST_STARTCAMERAINFERENCE.containing_type = _REQUEST
+_REQUEST_CAMERAINFERENCE.containing_type = _REQUEST
+_REQUEST_STOPCAMERAINFERENCE.containing_type = _REQUEST
+_REQUEST_IMUSELFTEST.containing_type = _REQUEST
+_REQUEST.fields_by_name['load_model'].message_type = _REQUEST_LOADMODEL
+_REQUEST.fields_by_name['unload_model'].message_type = _REQUEST_UNLOADMODEL
+_REQUEST.fields_by_name['image_inference'].message_type = _REQUEST_IMAGEINFERENCE
+_REQUEST.fields_by_name['start_camera_inference'].message_type = _REQUEST_STARTCAMERAINFERENCE
+_REQUEST.fields_by_name['camera_inference'].message_type = _REQUEST_CAMERAINFERENCE
+_REQUEST.fields_by_name['stop_camera_inference'].message_type = _REQUEST_STOPCAMERAINFERENCE
+_REQUEST.fields_by_name['imu_self_test'].message_type = _REQUEST_IMUSELFTEST
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['load_model'])
+_REQUEST.fields_by_name['load_model'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['unload_model'])
+_REQUEST.fields_by_name['unload_model'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['image_inference'])
+_REQUEST.fields_by_name['image_inference'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['start_camera_inference'])
+_REQUEST.fields_by_name['start_camera_inference'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['camera_inference'])
+_REQUEST.fields_by_name['camera_inference'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['stop_camera_inference'])
+_REQUEST.fields_by_name['stop_camera_inference'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_REQUEST.oneofs_by_name['request'].fields.append(
+ _REQUEST.fields_by_name['imu_self_test'])
+_REQUEST.fields_by_name['imu_self_test'].containing_oneof = _REQUEST.oneofs_by_name['request']
+_INFERENCERESULT_TENSORSENTRY.fields_by_name['value'].message_type = _FLOATTENSOR
+_INFERENCERESULT_TENSORSENTRY.containing_type = _INFERENCERESULT
+_INFERENCERESULT_FRAME.fields_by_name['image'].message_type = _BYTETENSOR
+_INFERENCERESULT_FRAME.containing_type = _INFERENCERESULT
+_INFERENCERESULT.fields_by_name['window'].message_type = _RECTANGLE
+_INFERENCERESULT.fields_by_name['tensors'].message_type = _INFERENCERESULT_TENSORSENTRY
+_INFERENCERESULT.fields_by_name['frame'].message_type = _INFERENCERESULT_FRAME
+_RESPONSE_ERROR.fields_by_name['type'].enum_type = _RESPONSE_ERROR_TYPE
+_RESPONSE_ERROR.containing_type = _RESPONSE
+_RESPONSE_ERROR_TYPE.containing_type = _RESPONSE_ERROR
+_RESPONSE.fields_by_name['errors'].message_type = _RESPONSE_ERROR
+_RESPONSE.fields_by_name['result'].message_type = _INFERENCERESULT
+DESCRIPTOR.message_types_by_name['Rectangle'] = _RECTANGLE
+DESCRIPTOR.message_types_by_name['TensorShape'] = _TENSORSHAPE
+DESCRIPTOR.message_types_by_name['ByteTensor'] = _BYTETENSOR
+DESCRIPTOR.message_types_by_name['FloatTensor'] = _FLOATTENSOR
+DESCRIPTOR.message_types_by_name['TensorNormalizer'] = _TENSORNORMALIZER
+DESCRIPTOR.message_types_by_name['Request'] = _REQUEST
+DESCRIPTOR.message_types_by_name['InferenceResult'] = _INFERENCERESULT
+DESCRIPTOR.message_types_by_name['Response'] = _RESPONSE
+
+Rectangle = _reflection.GeneratedProtocolMessageType('Rectangle', (_message.Message,), dict(
+ DESCRIPTOR = _RECTANGLE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Rectangle)
+ ))
+_sym_db.RegisterMessage(Rectangle)
+
+TensorShape = _reflection.GeneratedProtocolMessageType('TensorShape', (_message.Message,), dict(
+ DESCRIPTOR = _TENSORSHAPE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.TensorShape)
+ ))
+_sym_db.RegisterMessage(TensorShape)
+
+ByteTensor = _reflection.GeneratedProtocolMessageType('ByteTensor', (_message.Message,), dict(
+ DESCRIPTOR = _BYTETENSOR,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.ByteTensor)
+ ))
+_sym_db.RegisterMessage(ByteTensor)
+
+FloatTensor = _reflection.GeneratedProtocolMessageType('FloatTensor', (_message.Message,), dict(
+ DESCRIPTOR = _FLOATTENSOR,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.FloatTensor)
+ ))
+_sym_db.RegisterMessage(FloatTensor)
+
+TensorNormalizer = _reflection.GeneratedProtocolMessageType('TensorNormalizer', (_message.Message,), dict(
+ DESCRIPTOR = _TENSORNORMALIZER,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.TensorNormalizer)
+ ))
+_sym_db.RegisterMessage(TensorNormalizer)
+
+Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), dict(
+
+ LoadModel = _reflection.GeneratedProtocolMessageType('LoadModel', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_LOADMODEL,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.LoadModel)
+ ))
+ ,
+
+ UnloadModel = _reflection.GeneratedProtocolMessageType('UnloadModel', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_UNLOADMODEL,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.UnloadModel)
+ ))
+ ,
+
+ ImageInference = _reflection.GeneratedProtocolMessageType('ImageInference', (_message.Message,), dict(
+
+ ParamsEntry = _reflection.GeneratedProtocolMessageType('ParamsEntry', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_IMAGEINFERENCE_PARAMSENTRY,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.ImageInference.ParamsEntry)
+ ))
+ ,
+ DESCRIPTOR = _REQUEST_IMAGEINFERENCE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.ImageInference)
+ ))
+ ,
+
+ StartCameraInference = _reflection.GeneratedProtocolMessageType('StartCameraInference', (_message.Message,), dict(
+
+ ParamsEntry = _reflection.GeneratedProtocolMessageType('ParamsEntry', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.StartCameraInference.ParamsEntry)
+ ))
+ ,
+ DESCRIPTOR = _REQUEST_STARTCAMERAINFERENCE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.StartCameraInference)
+ ))
+ ,
+
+ CameraInference = _reflection.GeneratedProtocolMessageType('CameraInference', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_CAMERAINFERENCE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.CameraInference)
+ ))
+ ,
+
+ StopCameraInference = _reflection.GeneratedProtocolMessageType('StopCameraInference', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_STOPCAMERAINFERENCE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.StopCameraInference)
+ ))
+ ,
+
+ ImuSelfTest = _reflection.GeneratedProtocolMessageType('ImuSelfTest', (_message.Message,), dict(
+ DESCRIPTOR = _REQUEST_IMUSELFTEST,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request.ImuSelfTest)
+ ))
+ ,
+ DESCRIPTOR = _REQUEST,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Request)
+ ))
+_sym_db.RegisterMessage(Request)
+_sym_db.RegisterMessage(Request.LoadModel)
+_sym_db.RegisterMessage(Request.UnloadModel)
+_sym_db.RegisterMessage(Request.ImageInference)
+_sym_db.RegisterMessage(Request.ImageInference.ParamsEntry)
+_sym_db.RegisterMessage(Request.StartCameraInference)
+_sym_db.RegisterMessage(Request.StartCameraInference.ParamsEntry)
+_sym_db.RegisterMessage(Request.CameraInference)
+_sym_db.RegisterMessage(Request.StopCameraInference)
+_sym_db.RegisterMessage(Request.ImuSelfTest)
+
+InferenceResult = _reflection.GeneratedProtocolMessageType('InferenceResult', (_message.Message,), dict(
+
+ TensorsEntry = _reflection.GeneratedProtocolMessageType('TensorsEntry', (_message.Message,), dict(
+ DESCRIPTOR = _INFERENCERESULT_TENSORSENTRY,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.InferenceResult.TensorsEntry)
+ ))
+ ,
+
+ Frame = _reflection.GeneratedProtocolMessageType('Frame', (_message.Message,), dict(
+ DESCRIPTOR = _INFERENCERESULT_FRAME,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.InferenceResult.Frame)
+ ))
+ ,
+ DESCRIPTOR = _INFERENCERESULT,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.InferenceResult)
+ ))
+_sym_db.RegisterMessage(InferenceResult)
+_sym_db.RegisterMessage(InferenceResult.TensorsEntry)
+_sym_db.RegisterMessage(InferenceResult.Frame)
+
+Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), dict(
+
+ Error = _reflection.GeneratedProtocolMessageType('Error', (_message.Message,), dict(
+ DESCRIPTOR = _RESPONSE_ERROR,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Response.Error)
+ ))
+ ,
+ DESCRIPTOR = _RESPONSE,
+ __module__ = 'protocol_pb2'
+ # @@protoc_insertion_point(class_scope:spacepark.vision.Response)
+ ))
+_sym_db.RegisterMessage(Response)
+_sym_db.RegisterMessage(Response.Error)
+
+
+_REQUEST_IMAGEINFERENCE_PARAMSENTRY.has_options = True
+_REQUEST_IMAGEINFERENCE_PARAMSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001'))
+_REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY.has_options = True
+_REQUEST_STARTCAMERAINFERENCE_PARAMSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001'))
+_INFERENCERESULT_TENSORSENTRY.has_options = True
+_INFERENCERESULT_TENSORSENTRY._options = _descriptor._ParseOptions(descriptor_pb2.MessageOptions(), _b('8\001'))
+# @@protoc_insertion_point(module_scope)
diff --git a/src/examples/buzzer_demo.py b/src/examples/buzzer_demo.py
new file mode 100644
index 00000000..151b1c30
--- /dev/null
+++ b/src/examples/buzzer_demo.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A demo of the Piezo Buzzer."""
+
+import aiy.toneplayer
+
+
+def main():
+ tetris_theme = [
+ 'E5q',
+ 'Be',
+ 'C5e',
+ 'D5e',
+ 'E5s',
+ 'D5s',
+ 'C5s',
+ 'Be',
+ 'Bs',
+ 'Aq',
+ 'Ae',
+ 'C5e',
+ 'E5q',
+ 'D5e',
+ 'C5e',
+ 'Bq',
+ 'Be',
+ 'C5e',
+ 'D5q',
+ 'E5q',
+ 'C5q',
+ 'Aq',
+ 'Aq',
+ ]
+
+ player = aiy.toneplayer.TonePlayer(22)
+ player.play(*tetris_theme);
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/examples/vision/annotator.py b/src/examples/vision/annotator.py
new file mode 100644
index 00000000..a4f76fb2
--- /dev/null
+++ b/src/examples/vision/annotator.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Annotation library for drawing overlays on the raspberry pi's camera preview.
+
+Annotations include bounding boxes, text overlays, and points.
+Annotations support partial opacity, however only with respect to the content in
+the preview. A transparent fill value will cover up previously drawn overlay
+under it, but not the camera content under it. A color of None can be given,
+which will then not cover up overlay content drawn under the region.
+
+Note: Overlays do not persist through to the storage layer so images saved from
+the camera, will not contain overlays.
+"""
+
+import picamera
+import time
+from PIL import Image
+from PIL import ImageDraw
+
+
+def _round_to_bit(value, power):
+ """Rounds the given value to the next multiple of 2^power.
+
+ Args:
+ value: int to be rounded.
+ power: power of two which the value should be rounded up to.
+ Returns:
+ the result of value rounded to the next multiple 2^power.
+ """
+ return (((value - 1) >> power) + 1) << power
+
+
+def _round_buffer_dims(dims):
+ """Appropriately rounds the given dimensions for image overlaying.
+
+ The overlay buffer must be rounded the next multiple of 32 for the hight, and
+ the next multiple of 16 for the width."""
+ return (_round_to_bit(dims[0], 5), _round_to_bit(dims[1], 4))
+
+
+# TODO(namiller): Add an annotator for images.
+class Annotator(object):
+ """Utility for managing annotations on the camera preview.
+
+ Args:
+ camera: picamera.PiCamera camera object to overlay on top of.
+ bg_color: PIL.ImageColor (with alpha) for the background of the overlays.
+ default_color: PIL.ImageColor (with alpha) default for the drawn content.
+ """
+
+ def __init__(self, camera, bg_color=None, default_color=None,
+ dimensions=None):
+ self._dims = dimensions if dimensions else camera.resolution
+ self._buffer_dims = _round_buffer_dims(self._dims)
+ self._buffer = Image.new('RGBA', self._buffer_dims)
+ self._overlay = camera.add_overlay(
+ self._buffer.tobytes(), format='rgba', layer=3, size=self._buffer_dims)
+ self._draw = ImageDraw.Draw(self._buffer)
+ self._bg_color = bg_color if bg_color else (0, 0, 0, 0xA0)
+ self._default_color = default_color if default_color else (0xFF, 0, 0, 0xFF)
+
+ # MMALPort has a bug in enable.wrapper, where it always calls
+ # self._pool.send_buffer(block=False) regardless of the port direction.
+ # This is in contrast to setup time when it only calls
+ # self._pool.send_all_buffers(block=False)
+ # if self._port[0].type == mmal.MMAL_PORT_TYPE_OUTPUT.
+ # Because of this bug updating an overlay once will log a MMAL_EAGAIN
+ # error every update. This is safe to ignore as we the user is driving
+ # the renderer input port with calls to update() that dequeue buffers
+ # and sends them to the input port (so queue is empty on when
+ # send_all_buffers(block=False) is called from wrapper).
+ # As a workaround, monkey patch MMALPortPool.send_buffer and
+ # silence the "error" if thrown by our overlay instance.
+ original_send_buffer = picamera.mmalobj.MMALPortPool.send_buffer
+
+ def silent_send_buffer(zelf, **kwargs):
+ try:
+ original_send_buffer(zelf, **kwargs)
+ except picamera.exc.PiCameraMMALError as error:
+ # Only silence MMAL_EAGAIN for our target instance.
+ our_target = self._overlay.renderer.inputs[0].pool == zelf
+ if not our_target or error.status != 14:
+ raise error
+
+ picamera.mmalobj.MMALPortPool.send_buffer = silent_send_buffer
+
+ def update(self):
+ """Updates the contents of the overlay."""
+ self._overlay.update(self._buffer.tobytes())
+
+ def stop(self):
+ """Removes the overlay from the screen."""
+ self._draw.rectangle((0, 0) + self._dims, fill=0)
+ self.update()
+
+ def clear(self):
+ """Clears the contents of the overlay - leaving only the plain background.
+ """
+ self._draw.rectangle((0, 0) + self._dims, fill=self._bg_color)
+
+ def bounding_box(self, rect, outline=None, fill=None):
+ """Draws a bounding box around the specified rectangle.
+
+ Args:
+ rect: (x1, y1, x2, y2) rectangle to be drawn - where (x1,y1) and (x2, y2)
+ are opposite corners of the desired rectangle.
+ outline: PIL.ImageColor with which to draw the outline (defaults to the
+ configured default_color).
+ fill: PIL.ImageColor with which to fill the rectangel (defaults to None
+ which will not cover up drawings under the region.
+ """
+ outline = self._default_color if outline is None else outline
+ self._draw.rectangle(rect, fill=fill, outline=outline)
+
+ #TODO(namiller): Add a font size parameter and load a truetype font.
+ def text(self, location, text, color=None):
+ """Draws the given text at the given location.
+
+ Args:
+ location: (x,y) point at which to draw the text (upper left corner).
+ text: string to be drawn.
+ color: PIL.ImageColor to draw the string in (defaults to default_color).
+ """
+ color = self._default_color if color is None else color
+ self._draw.text(location, text, fill=color)
+
+ def point(self, location, radius=1, color=None):
+ """Draws a point of the given size at the given location.
+
+ Args:
+ location: (x,y) center of the point to be drawn.
+ radius: the radius of the point to be drawn.
+ color: The color to draw the point in (defaults to default_color).
+ """
+ color = self._default_color if color is None else color
+ self._draw.ellipse(
+ (location[0] - radius, location[1] - radius, location[0] + radius,
+ location[1] + radius),
+ fill=color)
+
+
+def _main():
+ """Example usage of the annotator utility.
+
+ Demonstrates setting up a camera preview, drawing slowly moving/intersecting
+ animations over it, and clearing the overlays."""
+ with picamera.PiCamera() as camera:
+ # Resolution can be arbitrary.
+ camera.resolution = (351, 561)
+ camera.start_preview()
+ annotator = Annotator(camera)
+ for i in range(10):
+ annotator.clear()
+ annotator.bounding_box(
+ (20, 20, 70, 70), outline=(0, 0xFF, 0, 0xFF), fill=0)
+ annotator.bounding_box((10 * i, 10, 10 * i + 50, 60))
+ annotator.bounding_box(
+ (80, 0, 130, 50), outline=(0, 0, 0xFF, 0xFF), fill=0)
+ annotator.text((100, 100), 'Hello World')
+ annotator.point((10, 100), radius=5)
+ annotator.update()
+ time.sleep(1)
+ annotator.stop()
+ time.sleep(10)
+
+
+if __name__ == '__main__':
+ _main()
diff --git a/src/examples/vision/face_camera_trigger.py b/src/examples/vision/face_camera_trigger.py
new file mode 100644
index 00000000..9a0ed9e9
--- /dev/null
+++ b/src/examples/vision/face_camera_trigger.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Trigger PiCamera when face is detected."""
+
+from aiy.vision.inference import CameraInference
+from aiy.vision.models import face_detection
+from picamera import PiCamera
+
+
+def main():
+ with PiCamera() as camera:
+ # Configure camera
+ camera.resolution = (1640, 922) # Full Frame, 16:9 (Camera v2)
+ camera.start_preview()
+
+ # Do inference on VisionBonnet
+ with CameraInference(face_detection.model()) as inference:
+ for result in inference.run():
+ if len(face_detection.get_faces(result)) >= 1:
+ camera.capture('faces.jpg')
+ break
+
+ # Stop preview
+ camera.stop_preview()
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/src/examples/vision/face_detection.py b/src/examples/vision/face_detection.py
new file mode 100644
index 00000000..f3d9c3d0
--- /dev/null
+++ b/src/examples/vision/face_detection.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Face detection library demo.
+
+ - Takes an input image and tries to detect faces.
+ - Draws bounding boxes around detected objects.
+ - Saves an image with bounding boxes around detected objects.
+"""
+import argparse
+from PIL import Image
+from PIL import ImageDraw
+
+from aiy.vision.inference import ImageInference
+from aiy.vision.models import face_detection
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--input', '-i', dest='input', required=True)
+ parser.add_argument('--output', '-o', dest='output')
+ args = parser.parse_args()
+
+ with ImageInference(face_detection.model()) as inference:
+ image = Image.open(args.input)
+ draw = ImageDraw.Draw(image)
+ for i, face in enumerate(face_detection.get_faces(inference.run(image))):
+ print('Face #%d: %s' % (i, str(face)))
+ x, y, width, height = face.bounding_box
+ draw.rectangle((x, y, x + width, y + height), outline='red')
+ if args.output:
+ image.save(args.output)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/examples/vision/face_detection_camera.py b/src/examples/vision/face_detection_camera.py
new file mode 100644
index 00000000..4f70afad
--- /dev/null
+++ b/src/examples/vision/face_detection_camera.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Camera inference face detection demo code.
+
+Runs continuous face detection on the VisionBonnet and prints the number of
+detected faces.
+
+Example:
+face_detection_camera.py --num_frames 10
+"""
+import argparse
+
+from aiy.vision.inference import CameraInference
+from aiy.vision.models import face_detection
+from examples.vision.annotator import Annotator
+from picamera import PiCamera
+
+
+def main():
+ """Face detection camera inference example."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--num_frames',
+ '-n',
+ type=int,
+ dest='num_frames',
+ default=-1,
+ help='Sets the number of frames to run for, otherwise runs forever.')
+ args = parser.parse_args()
+
+ with PiCamera() as camera:
+ # Forced sensor mode, 1640x1232, full FoV. See:
+ # https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes
+ # This is the resolution inference run on.
+ camera.sensor_mode = 4
+
+ # Scaled and cropped resolution. If different from sensor mode implied
+ # resolution, inference results must be adjusted accordingly. This is
+ # true in particular when camera.start_recording is used to record an
+ # encoded h264 video stream as the Pi encoder can't encode all native
+ # sensor resolutions, or a standard one like 1080p may be desired.
+ camera.resolution = (1640, 1232)
+
+ # Start the camera stream.
+ camera.framerate = 30
+ camera.start_preview()
+
+ # Annotator renders in software so use a smaller size and scale results
+ # for increased performace.
+ annotator = Annotator(camera, dimensions=(320, 240))
+ scale_x = 320 / 1640
+ scale_y = 240 / 1232
+
+ # Incoming boxes are of the form (x, y, width, height). Scale and
+ # transform to the form (x1, y1, x2, y2).
+ def transform(bounding_box):
+ x, y, width, height = bounding_box
+ return (scale_x * x, scale_y * y, scale_x * (x + width),
+ scale_y * (y + height))
+
+ with CameraInference(face_detection.model()) as inference:
+ for i, result in enumerate(inference.run()):
+ if i == args.num_frames:
+ break
+ faces = face_detection.get_faces(result)
+ annotator.clear()
+ for face in faces:
+ annotator.bounding_box(transform(face.bounding_box), fill=0)
+ annotator.update()
+ print('Iteration #%d: num_faces=%d' % (i, len(faces)))
+
+ camera.stop_preview()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/examples/vision/gpiozero/button_example.py b/src/examples/vision/gpiozero/button_example.py
new file mode 100644
index 00000000..8af353ed
--- /dev/null
+++ b/src/examples/vision/gpiozero/button_example.py
@@ -0,0 +1,26 @@
+"""Example code that demonstrates using a standard pin along with a hat pin.
+
+The button uses a standard GPIO pin through the raspberry pi's memory mapped io,
+while the led uses the hat's sysfs driver. This implemenation difference is
+transparent to the user.
+
+The demo will light up the on board LED whenever the user presses the button.
+"""
+from signal import pause
+from gpiozero import Button
+from gpiozero import LED
+from aiy.vision.pins import BUTTON_GPIO_PIN
+from aiy.vision.pins import LED_1
+
+# Set up a gpiozero LED using the first onboard LED on the vision hat.
+led = LED(LED_1)
+# Set up a gpiozero Button using the button included with the vision hat.
+button = Button(BUTTON_GPIO_PIN)
+
+# When the button is pressed, call the led.on() function (turn the led on)
+button.when_pressed = led.on
+# When the button is released, call the led.off() function (turn the led off)
+button.when_released = led.off
+
+# Wait for the user to kill the example.
+pause()
diff --git a/src/examples/vision/gpiozero/hat_button.py b/src/examples/vision/gpiozero/hat_button.py
new file mode 100644
index 00000000..fb47f19e
--- /dev/null
+++ b/src/examples/vision/gpiozero/hat_button.py
@@ -0,0 +1,26 @@
+"""Example code that demonstrates using a button connected through the hat.
+
+The button uses a hat pin through the sysfs driver illustrating the edge
+detection polling.
+
+The demo will light up the on board LED whenever PIN_D is drawn high.
+"""
+from signal import pause
+from gpiozero import Button
+from gpiozero import LED
+from aiy.vision.pins import LED_1
+from aiy.vision.pins import PIN_D
+
+
+# Set up a gpiozero LED using the first onboard LED on the vision hat.
+led = LED(LED_1)
+# Set up a gpiozero Button using the 4th pin on the vision hat expansion.
+button = Button(PIN_D)
+
+# When the button is pressed, call the led.on() function (turn the led on)
+button.when_pressed = led.on
+# When the button is released, call the led.off() function (turn the led off)
+button.when_released = led.off
+
+# Wait for the user to kill the example.
+pause()
diff --git a/src/examples/vision/gpiozero/led_example.py b/src/examples/vision/gpiozero/led_example.py
new file mode 100644
index 00000000..4d02cbf5
--- /dev/null
+++ b/src/examples/vision/gpiozero/led_example.py
@@ -0,0 +1,16 @@
+"""Demonstrates on board LED support with correct polarity.
+
+Demo will turn on, then off the first LED on the hat.
+"""
+
+from time import sleep
+from gpiozero import LED
+from aiy.vision.pins import LED_1
+
+led = LED(LED_1)
+# Alternate turning the LED off and on until the user terminates the example.
+while True:
+ led.on()
+ sleep(1)
+ led.off()
+ sleep(1)
diff --git a/src/examples/vision/gpiozero/servo_example.py b/src/examples/vision/gpiozero/servo_example.py
new file mode 100644
index 00000000..024f0231
--- /dev/null
+++ b/src/examples/vision/gpiozero/servo_example.py
@@ -0,0 +1,27 @@
+"""Demonstrates simultaneous control of two servos on the hat.
+
+One servo uses the simple default configuration, the other servo is tuned to
+ensure the full range is reachable.
+"""
+
+from time import sleep
+from gpiozero import Servo
+from aiy.vision.pins import PIN_A
+from aiy.vision.pins import PIN_B
+
+# Create a default servo that will not be able to use quite the full range.
+simple_servo = Servo(PIN_A)
+# Create a servo with the custom values to give the full dynamic range.
+tuned_servo = Servo(PIN_B, min_pulse_width=.0005, max_pulse_width=.0019)
+
+# Move the Servos back and forth until the user terminates the example.
+while True:
+ simple_servo.min()
+ tuned_servo.max()
+ sleep(1)
+ simple_servo.mid()
+ tuned_servo.mid()
+ sleep(1)
+ simple_servo.max()
+ tuned_servo.min()
+ sleep(1)
diff --git a/src/examples/vision/gpiozero/simple_button_example.py b/src/examples/vision/gpiozero/simple_button_example.py
new file mode 100644
index 00000000..5ef147b0
--- /dev/null
+++ b/src/examples/vision/gpiozero/simple_button_example.py
@@ -0,0 +1,23 @@
+"""Example code that demonstrates using a standard pin along with a hat pin.
+
+The button uses a standard GPIO pin through the raspberry pi's memory mapped io,
+while the led uses the hat's sysfs driver. This implemenation difference is
+transparent to the user.
+
+The demo will light up the on board LED whenever the user presses the button.
+"""
+from gpiozero import Button
+from gpiozero import LED
+from aiy.vision.pins import BUTTON_GPIO_PIN
+from aiy.vision.pins import LED_1
+
+# Set up a gpiozero LED using the first onboard LED on the vision hat.
+led = LED(LED_1)
+# Set up a gpiozero Button using the button included with the vision hat.
+button = Button(BUTTON_GPIO_PIN)
+
+while True:
+ if button.is_pressed:
+ led.on()
+ else:
+ led.off()
diff --git a/src/examples/vision/image_classification.py b/src/examples/vision/image_classification.py
new file mode 100644
index 00000000..896234f7
--- /dev/null
+++ b/src/examples/vision/image_classification.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Image classification library demo."""
+
+import argparse
+from PIL import Image
+
+from aiy.vision.inference import ImageInference
+from aiy.vision.models import image_classification
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--input', '-i', dest='input', required=True)
+ args = parser.parse_args()
+
+ with ImageInference(image_classification.model()) as inference:
+ image = Image.open(args.input)
+ classes = image_classification.get_classes(inference.run(image))
+ for i, (label, score) in enumerate(classes):
+ print('Result %d: %s (prob=%f)' % (i, label, score))
+
+if __name__ == '__main__':
+ main()
diff --git a/src/examples/vision/joy/install-services.sh b/src/examples/vision/joy/install-services.sh
new file mode 100644
index 00000000..1a92ae27
--- /dev/null
+++ b/src/examples/vision/joy/install-services.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+scripts_dir="$(dirname "${BASH_SOURCE[0]}")"
+cd "${scripts_dir}"
+
+sudo cp joy_detection_demo.service /lib/systemd/system
+systemctl enable joy_detection_demo.service
diff --git a/src/examples/vision/joy/joy_detection_demo.py b/src/examples/vision/joy/joy_detection_demo.py
new file mode 100644
index 00000000..93042663
--- /dev/null
+++ b/src/examples/vision/joy/joy_detection_demo.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Joy detection demo."""
+import argparse
+import collections
+import math
+import signal
+import sys
+import threading
+import time
+
+from aiy._drivers._hat import get_aiy_device_name
+from aiy._drivers._rgbled import PrivacyLED
+from aiy._drivers._rgbled import RGBLED
+from aiy.toneplayer import TonePlayer
+from aiy.vision.inference import CameraInference
+from aiy.vision.models import face_detection
+from picamera import PiCamera
+
+JOY_COLOR = (255, 70, 0)
+SAD_COLOR = (0, 0, 64)
+NONE_COLOR = (0, 0, 1)
+
+JOY_SCORE_PEAK = 0.85
+JOY_SCORE_MIN = 0.1
+
+JOY_SOUND = ['C5q', 'E5q', 'C6q']
+SAD_SOUND = ['C6q', 'E5q', 'C5q']
+MODEL_LOAD_SOUND = ['C6w', 'c6w', 'C6w']
+
+WINDOW_SIZE = 10
+
+
+def blend(color_a, color_b, alpha):
+ return tuple([
+ math.ceil(a * alpha + b * (1.0 - alpha))
+ for a, b in zip(color_a, color_b)
+ ])
+
+
+class JoyDetector(object):
+
+ def __init__(self, num_frames, preview_alpha):
+ self._rgbled = RGBLED(debug=False)
+ self._num_frames = num_frames
+ self._preview_alpha = preview_alpha
+ self._toneplayer = TonePlayer(22, bpm=10)
+ self._sound_played = False
+ self._detector = threading.Thread(target=self._run_detector)
+ self._animator = threading.Thread(target=self._run_animator)
+ self._joy_score_lock = threading.Lock()
+ self._joy_score = 0.0
+ self._joy_score_window = collections.deque(maxlen=WINDOW_SIZE)
+ self._run_event = threading.Event()
+ signal.signal(signal.SIGINT, lambda signal, frame: self.stop())
+ signal.signal(signal.SIGTERM, lambda signal, frame: self.stop())
+
+ @property
+ def joy_score(self):
+ with self._joy_score_lock:
+ return self._joy_score
+
+ @joy_score.setter
+ def joy_score(self, value):
+ with self._joy_score_lock:
+ self._joy_score = value
+
+ def start(self):
+ print('Starting JoyDetector...')
+ self._run_event.set()
+ self._detector.start()
+
+ def join(self):
+ self._detector.join()
+ self._animator.join()
+
+ def stop(self):
+ print('Stopping JoyDetector...')
+ self._run_event.clear()
+
+ def _play_sound(self, sound):
+ if not self._sound_played:
+ self._sound_played = True
+ self._sound = threading.Thread(target=self._toneplayer.play, args=(*sound,))
+ self._sound.start()
+
+ def _run_animator(self):
+ while self._run_event.is_set():
+ joy_score = self.joy_score
+ if joy_score > JOY_SCORE_PEAK:
+ self._play_sound(JOY_SOUND)
+ elif joy_score < JOY_SCORE_MIN:
+ self._play_sound(SAD_SOUND)
+ else:
+ self._sound_played = False
+
+ if joy_score > 0:
+ self._rgbled.SetColorMix(*blend(JOY_COLOR, SAD_COLOR, joy_score))
+ else:
+ self._rgbled.SetColorMix(*NONE_COLOR)
+ time.sleep(0.1)
+
+ def _run_detector(self):
+ with PiCamera() as camera, PrivacyLED():
+ # Forced sensor mode, 1640x1232, full FoV. See:
+ # https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes
+ # This is the resolution inference run on.
+ camera.sensor_mode = 4
+ camera.resolution = (1640, 1232)
+ camera.framerate = 15
+ # Blend the preview layer with the alpha value from the flags.
+ camera.start_preview(alpha=self._preview_alpha)
+ with CameraInference(face_detection.model()) as inference:
+ self._play_sound(MODEL_LOAD_SOUND)
+ self._animator.start()
+ for i, result in enumerate(inference.run()):
+ faces = face_detection.get_faces(result)
+ # Calculate joy score as an average for all detected faces.
+ joy_score = 0.0
+ if faces:
+ joy_score = sum([face.joy_score for face in faces]) / len(faces)
+
+ # Append new joy score to the window and calculate mean value.
+ self._joy_score_window.append(joy_score)
+ self.joy_score = sum(self._joy_score_window) / len(
+ self._joy_score_window)
+ if self._num_frames == i or not self._run_event.is_set():
+ break
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--num_frames',
+ '-n',
+ type=int,
+ dest='num_frames',
+ default=-1,
+ help='Sets the number of frames to run for. '
+ 'Setting this parameter to -1 will '
+ 'cause the demo to not automatically terminate.')
+ parser.add_argument(
+ '--preview_alpha',
+ '-pa',
+ type=int,
+ dest='preview_alpha',
+ default=0,
+ help='Sets the transparency value of the preview overlay (0-255).')
+ args = parser.parse_args()
+
+ device = get_aiy_device_name()
+ if not device or not 'Vision' in device:
+ print('Do you have an AIY Vision bonnet installed? Exiting.')
+ sys.exit(0)
+
+ detector = JoyDetector(args.num_frames, args.preview_alpha)
+ detector.start()
+ detector.join()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/examples/vision/joy/joy_detection_demo.service b/src/examples/vision/joy/joy_detection_demo.service
new file mode 100644
index 00000000..8325a7f9
--- /dev/null
+++ b/src/examples/vision/joy/joy_detection_demo.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=AIY Joy Detection Demo
+
+[Service]
+Type=simple
+Restart=no
+ExecStart=/home/pi/AIY-projects-python/env/bin/python /home/pi/AIY-projects-python/src/examples/vision/joy/joy_detection_demo.py
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/examples/vision/object_detection.py b/src/examples/vision/object_detection.py
new file mode 100644
index 00000000..ef04cfd3
--- /dev/null
+++ b/src/examples/vision/object_detection.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Object detection library demo.
+
+ - Takes an input image and tries to detect person, dog, or cat.
+ - Draws bounding boxes around detected objects.
+ - Saves an image with bounding boxes around detected objects.
+"""
+import argparse
+from PIL import Image
+from PIL import ImageDraw
+
+from aiy.vision.inference import ImageInference
+from aiy.vision.models import object_detection
+
+
+def _crop_center(image):
+ width, height = image.size
+ size = min(width, height)
+ x, y = (width - size) / 2, (height - size) / 2
+ return image.crop((x, y, x + size, y + size)), (x, y)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--input', '-i', dest='input', required=True)
+ parser.add_argument('--output', '-o', dest='output')
+ args = parser.parse_args()
+
+ with ImageInference(object_detection.model()) as inference:
+ image = Image.open(args.input)
+ image_center, offset = _crop_center(image)
+ draw = ImageDraw.Draw(image)
+ result = inference.run(image_center)
+ for i, obj in enumerate(object_detection.get_objects(result, 0.3, offset)):
+ print('Object #%d: %s' % (i, str(obj)))
+ x, y, width, height = obj.bounding_box
+ draw.rectangle((x, y, x + width, y + height), outline='red')
+ if args.output:
+ image.save(args.output)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/src/setup.py b/src/setup.py
new file mode 100644
index 00000000..8ea0f2d4
--- /dev/null
+++ b/src/setup.py
@@ -0,0 +1,33 @@
+from distutils.core import setup
+
+setup(
+ name='aiy',
+ version='0.1dev',
+ author='Peter Malkin',
+ author_email='petermalkin@google.com',
+ packages=[
+ 'aiy', 'aiy._apis', 'aiy._drivers', 'aiy.assistant', 'aiy.vision',
+ 'aiy.vision.models', 'aiy.vision.proto', 'aiy.test'
+ ],
+ url="https://aiyprojects.withgoogle.com/",
+ license='LICENSE.txt',
+ description="AIY Python API",
+ data_files=['README.md'],
+ long_description=open('README.md').read(),
+ scripts=[
+ "assistant_grpc_demo.py", "assistant_library_with_button_demo.py",
+ "cloudspeech_demo.py", "assistant_library_demo.py",
+ "assistant_library_with_local_commands_demo.py",
+ "examples/vision/joy/joy_detection_demo.py",
+ "examples/vision/annotator.py",
+ "examples/vision/face_detection.py",
+ "examples/vision/face_camera_trigger.py",
+ "examples/vision/object_detection.py",
+ "examples/vision/face_detection_camera.py",
+ "examples/vision/image_classification.py",
+ "examples/vision/gpiozero/button_example.py",
+ "examples/vision/gpiozero/hat_button.py",
+ "examples/vision/gpiozero/simple_button_example.py",
+ "examples/vision/gpiozero/led_example.py",
+ "examples/vision/gpiozero/servo_example.py"
+ ])
diff --git a/systemd/ntpdate.service b/systemd/ntpdate.service
index 2720f174..7db6d822 100644
--- a/systemd/ntpdate.service
+++ b/systemd/ntpdate.service
@@ -8,7 +8,7 @@ ExecStart=/usr/sbin/ntpdate -u pool.ntp.org
# we may not have network yet, so retry until success
Restart=on-failure
-RestartSec=3s
+RestartSec=60s
[Install]
WantedBy=multi-user.target
diff --git a/systemd/voice-recognizer.service b/systemd/voice-recognizer.service
index ba493d74..db0191ba 100644
--- a/systemd/voice-recognizer.service
+++ b/systemd/voice-recognizer.service
@@ -1,12 +1,15 @@
+# This service can be used to run your code automatically on startup. Look in
+# HACKING.md for instructions on creating main.py and enabling it.
+
[Unit]
Description=voice recognizer
After=network.target ntpdate.service
[Service]
-Environment=VIRTUAL_ENV=/home/pi/voice-recognizer-raspi/env
-Environment=PATH=/home/pi/voice-recognizer-raspi/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-ExecStart=/home/pi/voice-recognizer-raspi/env/bin/python3 -u src/main.py
-WorkingDirectory=/home/pi/voice-recognizer-raspi
+Environment=VIRTUAL_ENV=/home/pi/AIY-projects-python/env
+Environment=PATH=/home/pi/AIY-projects-python/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ExecStart=/home/pi/AIY-projects-python/env/bin/python3 -u src/main.py
+WorkingDirectory=/home/pi/AIY-projects-python
StandardOutput=inherit
StandardError=inherit
Restart=always