├── requirements.txt ├── README.md ├── .gitignore ├── streamlit_app.py └── LICENSE /requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow-cpu==2.12.0 2 | streamlit==1.23.1 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streamlit Demo: Deep Dream 2 | 3 | A [Streamlit](https://streamlit.io) demo demonstrating the Deep Dream technique. 4 | Adapted from the [TensorFlow Deep Dream 5 | tutorial](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/tutorials/deepdream) 6 | 7 | [![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://share.streamlit.io/streamlit/demo-deepdream) 8 | 9 | 10 | 11 | ## How to run this demo 12 | 13 | ``` 14 | pip install -r requirements.txt 15 | streamlit run https://raw.githubusercontent.com/tvst/deepdream/master/streamlit_app.py 16 | ``` 17 | 18 | ...or clone this repo and then run with: 19 | ``` 20 | streamlit run streamlit_app.py 21 | ``` 22 | 23 | ### Questions? Comments? 24 | 25 | Please ask in the [Streamlit community](https://discuss.streamlit.io). 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # ------------- 132 | 133 | models/ 134 | images/ 135 | -------------------------------------------------------------------------------- /streamlit_app.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | 3 | import numpy as np 4 | import PIL.Image 5 | import streamlit as st 6 | import tensorflow as tf 7 | 8 | "# Deep Dream :sleeping:" 9 | 10 | 11 | def download(url, max_dim=None): 12 | """Download an image from a URL and read it into a NumPy array.""" 13 | name = url.split("/")[-1] 14 | image_path = tf.keras.utils.get_file(name, origin=url) 15 | img = PIL.Image.open(image_path) 16 | if max_dim: 17 | img.thumbnail((max_dim, max_dim)) 18 | return np.array(img) 19 | 20 | 21 | def deprocess(img): 22 | """Normalize an image""" 23 | img = 255 * (img + 1.0) / 2.0 24 | return tf.cast(img, tf.uint8) 25 | 26 | 27 | def img_to_bytes(img): 28 | """Convert a PIL image to a byte array so users can download it""" 29 | with BytesIO() as buf: 30 | result.save(buf, format="PNG") 31 | img_bytes = buf.getvalue() 32 | return img_bytes 33 | 34 | 35 | def random_roll(img, maxroll): 36 | """Randomly shift the image to avoid tiled boundaries.""" 37 | shift = tf.random.uniform( 38 | shape=[2], minval=-maxroll, maxval=maxroll, dtype=tf.int32 39 | ) 40 | img_rolled = tf.roll(img, shift=shift, axis=[0, 1]) 41 | return shift, img_rolled 42 | 43 | 44 | @st.cache_resource 45 | def load_base_model(): 46 | return tf.keras.applications.InceptionV3(include_top=False, weights="imagenet") 47 | 48 | 49 | @st.cache_resource 50 | def load_all_layers(_model): 51 | return {layer.name: layer.output for layer in _model.layers} 52 | 53 | 54 | def load_dream_model(base_model, layers): 55 | return tf.keras.Model(inputs=base_model.input, outputs=layers) 56 | 57 | 58 | def show(dg, img): 59 | """Display an image.""" 60 | dg.image(PIL.Image.fromarray(np.array(img)), use_column_width=True) 61 | return dg 62 | 63 | 64 | # Below is the actual logic for this app. It's a bit messy because it's almost a 65 | # straight copy/paste from the original DeepDream example repo, which is also 66 | # messy: 67 | # https://github.com/tensorflow/docs/blob/9ae740ab7b5b3f9c32ca060332037b51d95674ae/site/en/tutorials/generative/deepdream.ipynb 68 | 69 | 70 | def calc_loss(img, model): 71 | """Pass forward the image through the model to retrieve the activations.""" 72 | img_batch = tf.expand_dims(img, axis=0) 73 | layer_activations = model(img_batch) 74 | if len(layer_activations) == 1: 75 | layer_activations = [layer_activations] 76 | 77 | losses = [] 78 | for act in layer_activations: 79 | loss = tf.math.reduce_mean(act) 80 | losses.append(loss) 81 | 82 | return tf.reduce_sum(losses) 83 | 84 | 85 | class TiledGradients(tf.Module): 86 | def __init__(self, model): 87 | self.model = model 88 | 89 | @tf.function( 90 | input_signature=( 91 | tf.TensorSpec(shape=[None, None, 3], dtype=tf.float32), 92 | tf.TensorSpec(shape=[2], dtype=tf.int32), 93 | tf.TensorSpec(shape=[], dtype=tf.int32), 94 | ) 95 | ) 96 | def __call__(self, img, img_size, tile_size=512): 97 | shift, img_rolled = random_roll(img, tile_size) 98 | gradients = tf.zeros_like(img_rolled) 99 | xs = tf.range(0, img_size[1], tile_size)[:-1] 100 | if not tf.cast(len(xs), bool): 101 | xs = tf.constant([0]) 102 | ys = tf.range(0, img_size[0], tile_size)[:-1] 103 | if not tf.cast(len(ys), bool): 104 | ys = tf.constant([0]) 105 | 106 | for x in xs: 107 | for y in ys: 108 | with tf.GradientTape() as tape: 109 | tape.watch(img_rolled) 110 | img_tile = img_rolled[y : y + tile_size, x : x + tile_size] 111 | loss = calc_loss(img_tile, self.model) 112 | gradients = gradients + tape.gradient(loss, img_rolled) 113 | 114 | gradients = tf.roll(gradients, shift=-shift, axis=[0, 1]) 115 | gradients /= tf.math.reduce_std(gradients) + 1e-8 116 | return gradients 117 | 118 | 119 | def run_deep_dream_with_octaves( 120 | img, steps_per_octave=100, step_size=0.01, octaves=range(-2, 3), octave_scale=1.3 121 | ): 122 | base_shape = tf.shape(img) 123 | img = tf.keras.utils.img_to_array(img) 124 | img = tf.keras.applications.inception_v3.preprocess_input(img) 125 | 126 | initial_shape = img.shape[:-1] 127 | img = tf.image.resize(img, initial_shape) 128 | 129 | text_template = "Octave: %s\n\nStep: %s" 130 | progress_widget = st.sidebar.progress(0) 131 | p = 0.0 132 | 133 | for octave in octaves: 134 | new_size = tf.cast(tf.convert_to_tensor(base_shape[:-1]), tf.float32) * ( 135 | octave_scale**octave 136 | ) 137 | new_size = tf.cast(new_size, tf.int32) 138 | img = tf.image.resize(img, new_size) 139 | for step in range(steps_per_octave): 140 | gradients = get_tiled_gradients(img, new_size) 141 | img = img + gradients * step_size 142 | img = tf.clip_by_value(img, -1, 1) 143 | p += 1 144 | progress_widget.progress( 145 | p / (steps_per_octave * len(octaves)), 146 | text_template % (octave, step + 1), 147 | ) 148 | if step % 10 == 0: 149 | show(image_widget, deprocess(img)) 150 | result = PIL.Image.fromarray(np.array(deprocess(img))) 151 | return result 152 | 153 | 154 | # Load and cache the base model and all layers 155 | base_model = load_base_model() 156 | all_layers = load_all_layers(base_model) 157 | 158 | st.sidebar.caption( 159 | """This Streamlit app is based entirely on 160 | [TensorFlow's DeepDream tutorial](https://github.com/tensorflow/docs/blob/9ae740ab7b5b3f9c32ca060332037b51d95674ae/site/en/tutorials/generative/deepdream.ipynb).""" 161 | ) 162 | 163 | # Define a dictionary for image sources 164 | image_sources = { 165 | "Specify image by URL...": None, 166 | "Upload image from my machine...": None, 167 | "Grace Hopper example": "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg", 168 | "Red sunflower example": "https://storage.googleapis.com/download.tensorflow.org/example_images/592px-Red_sunflower.jpg", 169 | "Yellow labrador example": "https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg", 170 | } 171 | 172 | image_source = st.sidebar.selectbox( 173 | "Input image", 174 | list(image_sources.keys()), 175 | help=""" 176 | Select an image to use as a starting point for Deep Dream.\n 177 | Either enter a URL to an image on the web, upload an image 178 | from your computer, or select one of the example images.""", 179 | ) 180 | 181 | if image_source == "Specify image by URL...": 182 | url = st.sidebar.text_input( 183 | "Image URL", 184 | image_sources["Yellow labrador example"], 185 | help="The URL of the image to use as a starting point for Deep Dream.", 186 | ) 187 | original_img = download(url, max_dim=500) 188 | 189 | elif image_source == "Upload image from my machine...": 190 | uploaded_file = st.sidebar.file_uploader( 191 | "Upload your own image...", 192 | type=["jpg", "png"], 193 | help="Upload an image from your computer to use as a starting point for Deep Dream.\n\nUses the same image as the 'Yellow labrador example' if no image is uploaded.", 194 | ) 195 | if uploaded_file is not None: 196 | original_img = PIL.Image.open(uploaded_file) 197 | original_img.thumbnail((500, 500)) 198 | original_img = np.array(original_img.copy()) 199 | else: 200 | original_img = download(image_sources["Yellow labrador example"], max_dim=500) 201 | 202 | else: 203 | original_img = download(image_sources[image_source], max_dim=500) 204 | 205 | octaves = st.sidebar.slider( 206 | "Octaves", 207 | min_value=-2, 208 | max_value=3, 209 | value=(-1, 0), 210 | step=1, 211 | help="The number of scales at which to run gradient ascent.", 212 | ) 213 | steps_per_octave = st.sidebar.slider( 214 | "Steps per Octave", 215 | min_value=10, 216 | max_value=100, 217 | value=50, 218 | step=10, 219 | help="The number of gradient ascent steps to run at each octave.", 220 | ) 221 | step_size = st.sidebar.slider( 222 | "Step Size", 223 | min_value=0.01, 224 | max_value=0.1, 225 | value=0.01, 226 | step=0.01, 227 | help="The step size for gradient ascent.", 228 | ) 229 | 230 | names = st.sidebar.multiselect( 231 | "Select layers to visualize", 232 | list(all_layers.keys()), 233 | default=["mixed3", "mixed5"], 234 | help=""" 235 | For DeepDream, the layers of interest are those where the convolutions are concatenated. 236 | There are 11 of these layers in InceptionV3, named 'mixed0' though 'mixed10'. \n\n 237 | Using different layers will result in different dream-like images. Deeper layers respond 238 | to higher-level features (such as eyes and faces), while earlier layers respond to simpler 239 | features (such as edges, shapes, and textures). 240 | Source: [TensorFlow DeepDream](https://www.tensorflow.org/tutorials/generative/deepdream?hl=en#prepare_the_feature_extraction_model)""", 241 | ) 242 | 243 | # Retrieve specific user-chosen layers from multiselect 244 | layers = [all_layers[name] for name in names] 245 | dream_model = load_dream_model(base_model, layers) 246 | get_tiled_gradients = TiledGradients(dream_model) 247 | 248 | st.subheader("Original Image") 249 | st.image(original_img, use_column_width=True) 250 | 251 | st.subheader("Deep Dream Image") 252 | image_widget = st.empty() 253 | result = run_deep_dream_with_octaves( 254 | img=original_img, 255 | steps_per_octave=steps_per_octave, 256 | step_size=step_size, 257 | octaves=octaves, 258 | ) 259 | 260 | img_bytes = img_to_bytes(result) 261 | 262 | st.download_button( 263 | label="Download Deep Dream Image", 264 | data=img_bytes, 265 | file_name="deep_dream.png", 266 | mime="image/png", 267 | ) 268 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------