{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Copyright 2020 Erik Härkönen. All rights reserved.\n", "# This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License. You may obtain a copy\n", "# of the License at http://www.apache.org/licenses/LICENSE-2.0\n", "\n", "# Unless required by applicable law or agreed to in writing, software distributed under\n", "# the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n", "# OF ANY KIND, either express or implied. See the License for the specific language\n", "# governing permissions and limitations under the License.\n", "\n", "%matplotlib inline\n", "from notebook_init import *\n", "from tqdm import trange\n", "\n", "out_root = Path('out/directions')\n", "makedirs(out_root, exist_ok=True)\n", "B = 5" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "# Model, layer, edit, layer_start, layer_end, class, sigma, idx, name, (example seeds)\n", "configs = [ \n", " ### StyleGAN2 cars\n", "\n", " # In paper\n", " ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'car', 20.0, 50, 'Autumn', [329004386]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 0, 4, 'car', -10, 15, 'Focal lendth', [587218105, 361309542, 1355448359]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 0, 9, 'car', 18.5, 44, 'Car model', [1204444821]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 7, 9, 'car', 20.0, 18, 'Reflections', [1498448887]),\n", " \n", " # Other\n", " # ('StyleGAN2', 'style', 'latent', 'w', 9, 11, 'car', -20.0, 41, 'Add grass', [257249032]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 5, 'car', -2.7, 0, 'Horizontal flip', [1221001524]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 16, 'car', 20.0, 50, 'Fall foliage', [1108802786]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'car', -14.0, 29, 'Blown out highlight', [490151100, 1010645708]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 4, 'car', 12, 13, 'Flat vs tall', [1541814754, 1355448359]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 5, 6, 'car', 20.0, 32, 'Front wheel turn', [1060866846]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 9, 10, 'car', -20.0, 35, 'Ground smoothness', [1920211941]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 16, 'car', 20.0, 37, 'Image contrast', [1419881462]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 9, 11, 'car', -20.0, 45, 'Sepia', [105288903]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 16, 'car', 20.0, 38, 'Sunset', [1419881462]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 5, 'car', -2.0, 1, 'Side to front', [1221001524]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 7, 'car', -7.5, 10, 'Sports car', [743765988]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'car', 5.3, 14, 'White car', [1355448359]),\n", " \n", "\n", " ### StyleGAN2 ffhq\n", "\n", " # In paper\n", " ('StyleGAN2', 'style', 'latent', 'w', 6, 8, 'ffhq', -20.0, 43, 'Disgusted', [140658858, 1887645531]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', 9.0, 0, 'Makeup', [266415229, 375122892]),\n", "\n", " # Other\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 5, 'ffhq', 10.0, 19, 'Big smile', [427229260]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 6, 8, 'ffhq', -20.0, 33, 'Scary eyes', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 2, 5, 'ffhq', 18.2, 21, 'Bald', [1635892780]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', 13.0, 13, 'Bright BG vs FG', [798602383]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 6, 'ffhq', -60.0, 47, 'Curly hair', [1140578688]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', -10.2, 16, 'Hair albedo', [427229260]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 7, 'ffhq', 10.0, 36, 'Displeased', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', 20.0, 37, 'Eyebrow thickness', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 8, 'ffhq', -30.0, 54, 'Eye openness', [11573701]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 5, 'ffhq', 20.0, 37, 'Face roundness', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 10, 'ffhq', -20.0, 54, 'Fearful eyes', [11573701]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 5, 'ffhq', -13.6, 21, 'Hairline', [1635892780]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 8, 'ffhq', 20.0, 30, 'Happy frizzy hair', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 1, 4, 'ffhq', -10.5, 11, 'Head angle up', [798602383]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 6, 'ffhq', -15.0, 23, 'In awe', [1635892780]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 6, 'ffhq', -15.0, 22, 'Large jaw', [1635892780]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 10, 11, 'ffhq', 20.0, 34, 'Lipstick', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 5, 'ffhq', -30.0, 51, 'Nose length', [11573701]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 18, 'ffhq', 5.0, 27, 'Overexposed', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 7, 'ffhq', -14.5, 35, 'Screaming', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 2, 6, 'ffhq', -20.0, 32, 'Short face', [1887645531]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 5, 'ffhq', -20.0, 46, 'Smile', [1175071341]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 5, 'ffhq', -20.0, 20, 'Unhappy bowl cut', [1635892780]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', -8.0, 10, 'Sunlight in face', [798602383]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 9, 'ffhq', -40.0, 58, 'Trimmed beard', [1602858467]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 3, 5, 'ffhq', -9.0, 20, 'Forehead hair', [1382206226]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 5, 'ffhq', -9.0, 21, 'Happy frizzy hair', [1382206226]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'ffhq', -15.0, 25, 'Light UD', [1382206226]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 11, 'ffhq', 9.0, 0, 'Makeup', [1953272274]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 4, 6, 'ffhq', -16.0, 36, 'Smile', [1382206226]),\n", "\n", "\n", " ### StyleGAN2 horse\n", "\n", " # In paper\n", " ('StyleGAN2', 'style', 'latent', 'w', 3, 5, 'horse', -2.9, 3, 'Add rider', [944988831]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 5, 7, 'horse', -7.8, 11, 'Coloring', [897830797]),\n", "\n", " # Other\n", " # ('StyleGAN2', 'style', 'latent', 'w', 7, 9, 'horse', 11.8, 20, 'White horse', [1042666993]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 9, 11, 'horse', 9.0, 8, 'Green bg', [897830797]),\n", " \n", "\n", " ### StyleGAN2 cat\n", "\n", " # In paper\n", " ('StyleGAN2', 'style', 'latent', 'w', 5, 8, 'cat', 20.0, 45, 'Eyes closed', [81011138]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 2, 5, 'cat', 20.0, 27, 'Fluffiness', [740196857]),\n", " \n", " # Other\n", " # ('StyleGAN2', 'style', 'latent', 'w', 0, 6, 'cat', 20.0, 18, 'Head dist 2', [2021386866]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'cat', 12.7, 28, 'Light pos', [740196857]),\n", " \n", "\n", " ### StyleGAN2 church\n", "\n", " # In paper\n", " ('StyleGAN2', 'style', 'latent', 'w', 7, 9, 'church', -20.0, 20, 'Clouds', [1360331956, 485108354]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 7, 9, 'church', -8.4, 8, 'Direct sunlight', [1777321344, 38689046]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 8, 9, 'church', 20.0, 15, 'Sun direction', [485108354]),\n", " ('StyleGAN2', 'style', 'latent', 'w', 12, 14, 'church', -20.0, 8, 'Vibrant', [373098621, 38689046]),\n", "\n", " # Other\n", " # ('StyleGAN2', 'style', 'latent', 'w', 9, 14, 'church', 9.9, 11, 'Blue skies', [1003401116]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 5, 7, 'church', -20.0, 20, 'Clouds 2', [1360331956, 485108354]),\n", " # ('StyleGAN2', 'style', 'latent', 'w', 5, 6, 'church', -19.1, 12, 'Trees', [1344303167]),\n", " \n", "\n", " ### StyleGAN1 bedrooms\n", "\n", " # In paper\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 0, 6, 'bedrooms', 18.5, 31, 'flat_vs_tall', [2073683729]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 0, 3, 'bedrooms', -2.6, 5, 'Bed pose', [96357868]),\n", " \n", "\n", " ### StyleGAN1 wikiart\n", "\n", " # In paper\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 0, 2, 'wikiart', -2.9, 7, 'Head rotation', [1819967864]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 8, 15, 'wikiart', 7.5, 9, 'Simple strokes', [1239190942]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 9, 15, 'wikiart', -20.0, 59, 'Skin tone', [1615931059, 1719766582]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 4, 7, 'wikiart', 20.0, 36, 'Mouth shape', [333293845]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 2, 4, 'wikiart', -35.0, 35, 'Eye spacing', [1213732031, 333293856]),\n", " ('StyleGAN', 'g_mapping', 'latent', 'w', 8, 15, 'wikiart', 20.0, 31, 'Sharpness', [1489906162, 1768450051]),\n", "\n", " # Other\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 4, 7, 'wikiart', -16.3, 25, 'Open mouth', [1655670048]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 10, 16, 'wikiart', -20.0, 18, 'Rough strokes', [1942295817]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 1, 4, 'wikiart', -7.2, 14, 'Camera UD', [1136416437]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 8, 14, 'wikiart', -8.4, 13, 'Stroke contrast', [1136416437]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 4, 7, 'wikiart', 20.0, 44, 'Eye size', [333293845]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 4, 8, 'wikiart', 13.9, 16, 'Open mouth', [2135985383]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 10, 15, 'wikiart', 20.0, 26, 'Sharpness 2', [1489906162, 1223183477]),\n", " # ('StyleGAN', 'g_mapping', 'latent', 'w', 9, 14, 'wikiart', 20.0, 32, 'Splotchy', [1768450051]),\n", " \n", "\n", " ### BigGAN-512\n", " \n", " # In paper\n", " ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 6, 10, 'red_fox', -20.0, 64, 'Add grass', [20736816]),\n", " ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 6, 15, 'barn', 9.0, 54, 'Hight contrast clouds', [1826867440]),\n", " ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 6, 15, 'leopard', -9.0, 37, 'Moonlight', [1202948959]),\n", " ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 3, 15, 'husky', -9.0, 62, 'Season', [1162727876]),\n", "\n", " # Other\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 4, 13, 'barn', 9.0, 51, 'Cloudy', [1516873095]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 5, 15, 'leopard', 9.0, 30, 'Dark bg', [1345197166]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 3, 12, 'red_fox', 11.8, 57, 'Dry ground', [1426778692]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 6, 15, 'leopard', -9.0, 41, 'Evening', [337748435]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 3, 7, 'husky', 9.0, 69, 'Grass bg', [701138437]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 0, 15, 'leopard', -4.9, 2, 'Head hight', [696403469]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 5, 10, 'red_fox', 20.0, 53, 'Large leaves', [1426778692]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 8, 9, 'husky', -20.0, 67, 'Lit up face', [513373036]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 12, 13, 'husky', 50.0, 46, 'Local contrast', [489408324]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 0, 4, 'leopard', -4.9, 12, 'On rock', [2044716610]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 4, 15, 'leopard', 9.0, 49, 'Orange foilage', [510622299]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 10, 11, 'leopard', -9.0, 46, 'Pixelated', [109524934]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 10, 11, 'leopard', 9.0, 43, 'Pixelated 2', [109524934]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 4, 13, 'barn', -9.0, 48, 'Colorful sky', [1516873095]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 6, 15, 'barn', 9.0, 65, 'Red barn', [1289115451]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 0, 15, 'leopard', -1.4, 3, 'Rotation 2', [696403469]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 14, 15, 'husky', 50.0, 46, 'Sharpness', [489408324]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 3, 4, 'husky', -20.0, 57, 'Show tongue', [489408324]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 5, 15, 'barn', -9.0, 44, 'Trees', [2121410149]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 4, 9, 'leopard', -9.0, 28, 'White bg', [1345197166]),\n", " # ('BigGAN-512', 'generator.gen_z', 'latent', 'z', 11, 14, 'husky', 20.0, 54, 'Washed out', [489408324]),\n", "]\n", "\n", "has_gpu = torch.cuda.is_available()\n", "device = torch.device('cuda' if has_gpu else 'cpu')\n", "\n", "num_imgs_per_example = 1\n", "\n", "for config_id, (model_name, layer, mode, latent_space, l_start, l_end, classname, sigma, idx, title, seeds) in enumerate(configs[:]):\n", " print(f'{model_name}, {layer}, {title}')\n", " \n", " inst = get_instrumented_model(model_name, classname, layer, device, inst=inst) # reuse if possible\n", " model = inst.model\n", " \n", " if 'BigGAN' in model_name:\n", " model.truncation = 0.6\n", " elif 'StyleGAN2' in model_name:\n", " model.truncation = 0.7\n", " \n", " if latent_space == 'w':\n", " model.use_w()\n", " elif hasattr(model, 'use_z'):\n", " model.use_z()\n", " \n", " # Load or compute decomposition\n", " config = Config(\n", " output_class = classname,\n", " model = model_name,\n", " layer = layer,\n", " estimator = 'ipca',\n", " use_w = (latent_space == 'w'),\n", " n = 1_000_000\n", " )\n", "\n", " # Special case: BigGAN512-deep, gen_z: class-independent\n", " if model_name == 'BigGAN-512' and layer == 'generator.gen_z':\n", " config.output_class = 'husky' # chosen class doesn't matter\n", " \n", " dump_name = get_or_compute(config, inst)\n", " data = np.load(dump_name, allow_pickle=False)\n", " X_comp = data['act_comp']\n", " X_global_mean = data['act_mean']\n", " X_stdev = data['act_stdev']\n", " Z_global_mean = data['lat_mean']\n", " Z_comp = data['lat_comp']\n", " Z_stdev = data['lat_stdev']\n", " data.close()\n", "\n", " model.set_output_class(classname)\n", " feat_shape = X_comp[0].shape\n", " sample_dims = np.prod(feat_shape)\n", " \n", " # Transfer to GPU\n", " components = SimpleNamespace(\n", " X_comp = torch.from_numpy(X_comp).view(-1, *feat_shape).to('cuda').float(), #-1, 1, C, H, W\n", " X_global_mean = torch.from_numpy(X_global_mean).view(*feat_shape).to('cuda').float(), # 1, C, H, W\n", " X_stdev = torch.from_numpy(X_stdev).to('cuda').float(),\n", " Z_comp = torch.from_numpy(Z_comp).to('cuda').float(),\n", " Z_stdev = torch.from_numpy(Z_stdev).to('cuda').float(),\n", " Z_global_mean = torch.from_numpy(Z_global_mean).to('cuda').float(),\n", " )\n", " \n", " num_seeds = ((num_imgs_per_example - 1) // B + 1) * B # make divisible\n", " max_seed = np.iinfo(np.int32).max\n", " seeds = np.concatenate((seeds, np.random.randint(0, max_seed, num_seeds)))\n", " seeds = seeds[:num_seeds].astype(np.int32)\n", " latents = [model.sample_latent(1, seed=s) for s in seeds]\n", " \n", " # Range is exclusive, in contrast to notation in paper\n", " edit_start = l_start\n", " edit_end = model.get_max_latents() if l_end == -1 else l_end\n", " \n", " batch_frames = create_strip_centered(inst, mode, layer, latents, components.X_comp[idx],\n", " components.Z_comp[idx], components.X_stdev[idx], components.Z_stdev[idx],\n", " components.X_global_mean, components.Z_global_mean, sigma, edit_start, edit_end)\n", " #save_frames(f'{config_id}_{title}_{mode}', model_name, out_root, batch_frames)\n", " \n", " edit_name = prettify_name(title)\n", " outidr = out_root / model_name / classname / edit_name\n", " makedirs(outidr, exist_ok=True)\n", "\n", " for ex, frames in enumerate(batch_frames):\n", " for i, frame in enumerate(frames):\n", " Image.fromarray(np.uint8(frame*255)).save(outidr / f'cmp{idx}_s{edit_start}_e{edit_end}_{seeds[ex]}_{i}.png')\n", "\n", " # Show first\n", " plt.figure(figsize=(15,15))\n", " plt.imshow(np.hstack(pad_frames(batch_frames[0])))\n", " plt.axis('off')\n", " plt.show()\n", "\n", "print('Done')\n", " \n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Convert saved directions to textual form (for pasting in cell above)\n", "\n", "templ = \"{active}('{model_name}', '{layer}', '{edit_type}', '{latent_space}', {edit_start}, {edit_end}, '{out_class}', {sigma}, {comp_idx}, '{description}', [{seeds}]),\"\n", "def textual_repr(dump):\n", " comp_cls = dump['decomposition']['class_name'] # PCA computed from\n", " appl_cls = dump['output_class'] # components applied onto\n", " \n", " return templ.format(\n", " active = '#' if comp_cls != appl_cls else '', # don't mix\n", " model_name = dump['model_name'],\n", " layer = dump['decomposition']['layer'],\n", " edit_type = dump['edit_type'],\n", " latent_space = dump['latent_space'].lower(),\n", " edit_start = dump['edit_start'],\n", " edit_end = dump['edit_end'],\n", " out_class = comp_cls,\n", " sigma = dump['sigma_range'],\n", " comp_idx = dump['component_index'],\n", " description = dump['name'],\n", " seeds = dump['example_seed']\n", " )\n", "\n", "import pickle\n", "import glob\n", "\n", "dumps_root = Path('../out/directions')\n", "config_files = glob.glob(f'{dumps_root.resolve()}/*.pkl')\n", "\n", "for config_id, dump_path in enumerate(config_files):\n", " with open(dump_path, 'rb') as f:\n", " data = pickle.load(f)\n", " desc = textual_repr(data)\n", " print(desc)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.5-final" } }, "nbformat": 4, "nbformat_minor": 2 }