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