jgwill commited on
Commit
cf3365e
1 Parent(s): 88261e5
Files changed (4) hide show
  1. Dockerfile +23 -0
  2. compo-singleone-v1-dev-acc.py +368 -0
  3. requirements.txt +69 -0
  4. server.py +59 -0
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM guillaumeai/ast:kirchner
2
+
3
+ # RUN apt update
4
+ # RUN apt upgrade -y
5
+
6
+ RUN pip install -U pip
7
+ # #RUN pip install -U pyyaml
8
+ # RUN pip install -U runway-python
9
+ # #runway --force-reinstall
10
+ # #RUN pip install -U tensorflow
11
+
12
+
13
+ COPY requirements.txt .
14
+ RUN pip install -r requirements.txt
15
+
16
+ COPY server.py .
17
+ COPY compo-singleone-v1-dev-acc.py .
18
+
19
+ EXPOSE 7860
20
+
21
+ #compo-singleone-v1-dev-acc.py
22
+
23
+ CMD ["python", "compo-singleone-v1-dev-acc.py"]
compo-singleone-v1-dev-acc.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #####################################################
2
+ # AST Composite Server Double Two
3
+ # By Guillaume Descoteaux-Isabelle, 20021
4
+ #
5
+ # This server compose two Adaptive Style Transfer model (output of the first pass serve as input to the second using the same model)
6
+ ########################################################
7
+ #v1-dev
8
+ #Receive the 2 res from arguments in the request...
9
+
10
+
11
+ import os
12
+ import numpy as np
13
+ import tensorflow as tf
14
+ import cv2
15
+ from module import encoder, decoder
16
+ from glob import glob
17
+ import runway
18
+ from runway.data_types import number, text
19
+
20
+
21
+ #from utils import *
22
+ import scipy
23
+ from datetime import datetime
24
+ import time
25
+
26
+
27
+
28
+
29
+ # Determining the size of the passes
30
+ pass1_image_size = 1328
31
+ if not os.getenv('PASS1IMAGESIZE'):
32
+ print("PASS1IMAGESIZE env var non existent;using default:" + str(pass1_image_size))
33
+ else:
34
+ pass1_image_size = os.getenv('PASS1IMAGESIZE', 1328)
35
+ print("PASS1IMAGESIZE value:" + str(pass1_image_size))
36
+
37
+
38
+ # Determining the size of the passes
39
+ autoabc = 1
40
+ if not os.getenv('AUTOABC'):
41
+ print("AUTOABC env var non existent;using default:")
42
+ print(autoabc)
43
+ abcdefault = 1
44
+ print("NOTE----> when running docker, set AUTOABC variable")
45
+ print(" docker run ... -e AUTOABC=1 #enabled, 0 to disabled (default)")
46
+ else:
47
+ autoabc = os.getenv('AUTOABC',1)
48
+ print("AUTOABC value:")
49
+ print(autoabc)
50
+ abcdefault = autoabc
51
+
52
+
53
+ #pass2_image_size = 1024
54
+ #if not os.getenv('PASS2IMAGESIZE'):
55
+ # print("PASS2IMAGESIZE env var non existent;using default:" + pass2_image_size)
56
+ #else:
57
+ # pass2_image_size = os.getenv('PASS2IMAGESIZE')
58
+ # print("PASS2IMAGESIZE value:" + pass2_image_size)
59
+
60
+ # pass3_image_size = 2048
61
+ # if not os.getenv('PASS3IMAGESIZE'):
62
+ # print("PASS3IMAGESIZE env var non existent;using default:" + pass3_image_size)
63
+ # else:
64
+ # pass3_image_size = os.getenv('PASS3IMAGESIZE')
65
+ # print("PASS3IMAGESIZE value:" + pass3_image_size)
66
+
67
+ ##########################################
68
+ ## MODELS
69
+ #model name for sending it in the response
70
+ model1name = "UNNAMED"
71
+ if not os.getenv('MODEL1NAME'):
72
+ print("MODEL1NAME env var non existent;using default:" + model1name)
73
+ else:
74
+ model1name = os.getenv('MODEL1NAME', "UNNAMED")
75
+ print("MODEL1NAME value:" + model1name)
76
+
77
+ # #m2
78
+ # model2name = "UNNAMED"
79
+ # if not os.getenv('MODEL2NAME'): print("MODEL2NAME env var non existent;using default:" + model2name)
80
+ # else:
81
+ # model2name = os.getenv('MODEL2NAME')
82
+ # print("MODEL2NAME value:" + model2name)
83
+
84
+ # #m3
85
+ # model3name = "UNNAMED"
86
+ # if not os.getenv('MODEL3NAME'): print("MODEL3NAME env var non existent;using default:" + model3name)
87
+ # else:
88
+ # model3name = os.getenv('MODEL3NAME')
89
+ # print("MODEL3NAME value:" + model3name)
90
+
91
+ #######################################################
92
+
93
+
94
+ #########################################################
95
+ # SETUP
96
+
97
+ @runway.setup(options={'styleCheckpoint': runway.file(is_directory=True)})
98
+ def setup(opts):
99
+ sess = tf.Session()
100
+ # sess2 = tf.Session()
101
+ # sess3 = tf.Session()
102
+ init_op = tf.global_variables_initializer()
103
+ # init_op2 = tf.global_variables_initializer()
104
+ # init_op3 = tf.global_variables_initializer()
105
+ sess.run(init_op)
106
+ # sess2.run(init_op2)
107
+ # sess3.run(init_op3)
108
+ with tf.name_scope('placeholder'):
109
+ input_photo = tf.placeholder(dtype=tf.float32,
110
+ shape=[1, None, None, 3],
111
+ name='photo')
112
+ input_photo_features = encoder(image=input_photo,
113
+ options={'gf_dim': 32},
114
+ reuse=False)
115
+ output_photo = decoder(features=input_photo_features,
116
+ options={'gf_dim': 32},
117
+ reuse=False)
118
+ saver = tf.train.Saver()
119
+ # saver2 = tf.train.Saver()
120
+ # saver3 = tf.train.Saver()
121
+ path = opts['styleCheckpoint']
122
+ #Getting the model name
123
+ model_name = [p for p in os.listdir(path) if os.path.isdir(os.path.join(path, p))][0]
124
+ if not os.getenv('MODELNAME'):
125
+ dtprint("CONFIG::MODELNAME env var non existent;using default:" + model_name)
126
+ else:
127
+ model_name = os.getenv('MODELNAME')
128
+
129
+
130
+
131
+ # #Getting the model2 name
132
+ # model2_name = [p for p in os.listdir(path) if os.path.isdir(os.path.join(path, p))][1]
133
+ # if not os.getenv('MODEL2NAME'):
134
+ # dtprint("CONFIG::MODEL2NAME env var non existent;using default:" + model2_name)
135
+ # else:
136
+ # model2_name = os.getenv('MODEL2NAME')
137
+
138
+
139
+ ##Getting the model3 name
140
+ # model3_name = [p for p in os.listdir(path) if os.path.isdir(os.path.join(path, p))][2]
141
+ # if not os.getenv('MODEL3NAME'):
142
+ # dtprint("CONFIG::MODEL3NAME env var non existent;using default:" + model3_name)
143
+ # else:
144
+ # model3_name = os.getenv('MODEL3NAME')
145
+
146
+
147
+
148
+ checkpoint_dir = os.path.join(path, model_name, 'checkpoint_long')
149
+ #checkpoint2_dir = os.path.join(path, model2_name, 'checkpoint_long')
150
+ # checkpoint3_dir = os.path.join(path, model3_name, 'checkpoint_long')
151
+ print("-----------------------------------------")
152
+ print("modelname is : " + model_name)
153
+ #print("model2name is : " + model2_name)
154
+ # print("model3name is : " + model3_name)
155
+ print("checkpoint_dir is : " + checkpoint_dir)
156
+
157
+ print("Auto Brightness-Contrast Correction can be set as the x2 of this SingleOne Server")
158
+
159
+
160
+ #print("checkpoint2_dir is : " + checkpoint2_dir)
161
+ # print("checkpoint3_dir is : " + checkpoint3_dir)
162
+ print("-----------------------------------------")
163
+ ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
164
+ #ckpt2 = tf.train.get_checkpoint_state(checkpoint2_dir)
165
+ # ckpt3 = tf.train.get_checkpoint_state(checkpoint3_dir)
166
+ ckpt_name = os.path.basename(ckpt.model_checkpoint_path)
167
+ #ckpt2_name = os.path.basename(ckpt2.model_checkpoint_path)
168
+ # ckpt3_name = os.path.basename(ckpt3.model_checkpoint_path)
169
+ saver.restore(sess, os.path.join(checkpoint_dir, ckpt_name))
170
+ #saver2.restore(sess2, os.path.join(checkpoint2_dir, ckpt2_name))
171
+ # saver3.restore(sess3, os.path.join(checkpoint3_dir, ckpt3_name))
172
+ m1 = dict(sess=sess, input_photo=input_photo, output_photo=output_photo)
173
+ #m2 = dict(sess=sess2, input_photo=input_photo, output_photo=output_photo)
174
+ # m3 = dict(sess=sess3, input_photo=input_photo, output_photo=output_photo)
175
+ models = type('', (), {})()
176
+ models.m1 = m1
177
+ #models.m2 = m2
178
+ # models.m3 = m3
179
+ return models
180
+
181
+
182
+ #@STCGoal add number or text to specify resolution of the three pass
183
+ inputs={'contentImage': runway.image,'x1':number(default=1024,min=24,max=17000),'x2':number(default=0,min=-99,max=99)}
184
+ outputs={'stylizedImage': runway.image,'totaltime':number,'x1': number,'c1':number,'model1name':text}
185
+
186
+ @runway.command('stylize', inputs=inputs, outputs=outputs)
187
+ def stylize(models, inp):
188
+ start = time.time()
189
+ dtprint("Composing.1..")
190
+ model = models.m1
191
+ #model2 = models.m2
192
+ # model3 = models.m3
193
+
194
+ #Getting our names back (even though I think we dont need)
195
+ #@STCIssue BUGGED
196
+ # m1name=models.m1.name
197
+ # m2name=models.m2.name
198
+ # m3name=models.m3.name
199
+
200
+ #get size from inputs rather than env
201
+ x1 = inp['x1']
202
+ c1 = inp['x2']
203
+ # x3 = inp['x3']
204
+ if c1 > 99:
205
+ ci = abcdefault
206
+
207
+
208
+ #
209
+ img = inp['contentImage']
210
+ img = np.array(img)
211
+ img = img / 127.5 - 1.
212
+
213
+ #@a Pass 1 RESIZE to 1368px the smaller side
214
+ image_size=pass1_image_size
215
+ image_size=x1
216
+ img_shape = img.shape[:2]
217
+ alpha = float(image_size) / float(min(img_shape))
218
+ dtprint ("DEBUG::content.imgshape:" + str(tuple(img_shape)) + ", alpha:" + str(alpha))
219
+
220
+ try:
221
+ img = scipy.misc.imresize(img, size=alpha)
222
+ except:
223
+ pass
224
+
225
+
226
+ img = np.expand_dims(img, axis=0)
227
+ #@a INFERENCE PASS 1
228
+ dtprint("INFO:Pass1 inference starting")
229
+ img = model['sess'].run(model['output_photo'], feed_dict={model['input_photo']: img})
230
+ dtprint("INFO:Pass1 inference done")
231
+ #
232
+ img = (img + 1.) * 127.5
233
+ img = img.astype('uint8')
234
+ img = img[0]
235
+ #dtprint("INFO:Upresing Pass1 for Pass 2 (STARTING) ")
236
+
237
+ #@a Pass 2 RESIZE to 1024px the smaller side
238
+ #image_size=pass2_image_size
239
+ #image_size=x2
240
+ #img_shape = img.shape[:2]
241
+
242
+
243
+ #alpha = float(image_size) / float(min(img_shape))
244
+ #dtprint ("DEBUG::pass1.imgshape:" + str(tuple(img_shape)) + ", alpha:" + str(alpha))
245
+
246
+ #img = scipy.misc.imresize(img, size=alpha)
247
+ #dtprint("INFO:Upresing Pass1 (DONE) ")
248
+
249
+ #Iteration 2
250
+ #img = np.array(img)
251
+ #img = img / 127.5 - 1.
252
+ #img = np.expand_dims(img, axis=0)
253
+ #@a INFERENCE PASS 2 using the same model
254
+ #dtprint("INFO:Pass2 inference (STARTING)")
255
+ #img = model['sess'].run(model['output_photo'], feed_dict={model['input_photo']: img})
256
+ #dtprint("INFO:Pass2 inference (DONE)")
257
+ #img = (img + 1.) * 127.5
258
+ #img = img.astype('uint8')
259
+ #img = img[0]
260
+
261
+
262
+
263
+ # #pass3
264
+
265
+ # #@a Pass 3 RESIZE to 2048px the smaller side
266
+ # image_size=pass3_image_size
267
+ # image_size=x3
268
+ # img_shape = img.shape[:2]
269
+
270
+
271
+ # alpha = float(image_size) / float(min(img_shape))
272
+ # dtprint ("DEBUG::pass2.imgshape:" + str(tuple(img_shape)) + ", alpha:" + str(alpha))
273
+
274
+ # img = scipy.misc.imresize(img, size=alpha)
275
+ # dtprint("INFO:Upresing Pass2 (DONE) ")
276
+
277
+ # #Iteration 3
278
+ # img = np.array(img)
279
+ # img = img / 127.5 - 1.
280
+ # img = np.expand_dims(img, axis=0)
281
+ # #@a INFERENCE PASS 3
282
+ # dtprint("INFO:Pass3 inference (STARTING)")
283
+ # img = model3['sess'].run(model3['output_photo'], feed_dict={model3['input_photo']: img})
284
+ # dtprint("INFO:Pass3 inference (DONE)")
285
+ # img = (img + 1.) * 127.5
286
+ # img = img.astype('uint8')
287
+ # img = img[0]
288
+ # #pass3
289
+
290
+ #dtprint("INFO:Composing done")
291
+ print('autoabc value:')
292
+ print(c1)
293
+ if c1 != 0 :
294
+ print('Auto Brightening images...')
295
+ img = img, alpha2, beta = automatic_brightness_and_contrast(img,c1)
296
+
297
+ stop = time.time()
298
+ totaltime = stop - start
299
+ print("The time of the run:", totaltime)
300
+ res2 = dict(stylizedImage=img,totaltime=totaltime,x1=x1,model1name=model1name,c1=c1)
301
+ return res2
302
+
303
+
304
+
305
+ def dtprint(msg):
306
+ dttag=getdttag()
307
+ print(dttag + "::" + msg )
308
+
309
+ def getdttag():
310
+ # datetime object containing current date and time
311
+ now = datetime.now()
312
+
313
+ # dd/mm/YY H:M:S
314
+ # dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
315
+ return now.strftime("%H:%M:%S")
316
+
317
+
318
+
319
+ # Automatic brightness and contrast optimization with optional histogram clipping
320
+ def automatic_brightness_and_contrast(image, clip_hist_percent=25):
321
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
322
+
323
+ # Calculate grayscale histogram
324
+ hist = cv2.calcHist([gray],[0],None,[256],[0,256])
325
+ hist_size = len(hist)
326
+
327
+ # Calculate cumulative distribution from the histogram
328
+ accumulator = []
329
+ accumulator.append(float(hist[0]))
330
+ for index in range(1, hist_size):
331
+ accumulator.append(accumulator[index -1] + float(hist[index]))
332
+
333
+ # Locate points to clip
334
+ maximum = accumulator[-1]
335
+ clip_hist_percent *= (maximum/100.0)
336
+ clip_hist_percent /= 2.0
337
+
338
+ # Locate left cut
339
+ minimum_gray = 0
340
+ while accumulator[minimum_gray] < clip_hist_percent:
341
+ minimum_gray += 1
342
+
343
+ # Locate right cut
344
+ maximum_gray = hist_size -1
345
+ while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
346
+ maximum_gray -= 1
347
+
348
+ # Calculate alpha and beta values
349
+ alpha = 255 / (maximum_gray - minimum_gray)
350
+ beta = -minimum_gray * alpha
351
+
352
+ '''
353
+ # Calculate new histogram with desired range and show histogram
354
+ new_hist = cv2.calcHist([gray],[0],None,[256],[minimum_gray,maximum_gray])
355
+ plt.plot(hist)
356
+ plt.plot(new_hist)
357
+ plt.xlim([0,256])
358
+ plt.show()
359
+ '''
360
+
361
+ auto_result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
362
+ return (auto_result, alpha, beta)
363
+
364
+
365
+ if __name__ == '__main__':
366
+ #print('External Service port is:' +os.environ.get('SPORT'))
367
+ os.environ["RW_PORT"] = "7860"
368
+ runway.run()
requirements.txt ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==0.9.0
2
+ asn1crypto==0.22.0
3
+ astor==0.8.1
4
+ backports.weakref==1.0.post1
5
+ Brotli==1.0.7
6
+ certifi==2019.11.28
7
+ cffi==1.10.0
8
+ chardet==3.0.4
9
+ click==7.1.2
10
+ colorcet==2.0.2
11
+ conda==4.8.3
12
+ conda-package-handling==1.6.0
13
+ cryptography==1.8.1
14
+ enum34==1.1.6
15
+ Flask==1.1.2
16
+ Flask-Compress==1.5.0
17
+ Flask-Cors==3.0.8
18
+ Flask-Sockets==0.2.1
19
+ funcsigs==1.0.2
20
+ functools32==3.2.3.post2
21
+ futures==3.3.0
22
+ gast==0.2.2
23
+ gevent==20.6.2
24
+ gevent-websocket==0.10.1
25
+ google-pasta==0.2.0
26
+ greenlet==0.4.16
27
+ grpcio==1.30.0
28
+ h5py==2.10.0
29
+ idna==2.6
30
+ ipaddress==1.0.18
31
+ itsdangerous==1.1.0
32
+ Jinja2==2.11.2
33
+ Keras-Applications==1.0.8
34
+ Keras-Preprocessing==1.1.2
35
+ Markdown==3.1.1
36
+ MarkupSafe==1.1.1
37
+ mock==3.0.5
38
+ numpy==1.15.0
39
+ opencv-python==4.2.0.32
40
+ opt-einsum==2.3.2
41
+ packaging==16.8
42
+ param==1.9.3
43
+ Pillow==6.2.2
44
+ protobuf==3.12.2
45
+ pycosat==0.6.3
46
+ pycparser==2.18
47
+ pycrypto==2.6.1
48
+ pyct==0.4.6
49
+ pyOpenSSL==17.0.0
50
+ pyparsing==2.2.0
51
+ PySocks==1.7.1
52
+ PyYAML==3.11
53
+ requests @ file:///tmp/build/80754af9/requests_1592841827918/work
54
+ runway-model-runner @ file:///root/runner
55
+ runway-python==0.5.9
56
+ scipy==1.1.0
57
+ six==1.15.0
58
+ tensorboard==1.15.0
59
+ tensorflow==1.15.0
60
+ tensorflow-estimator==1.15.1
61
+ termcolor==1.1.0
62
+ tqdm @ file:///tmp/build/80754af9/tqdm_1593446365756/work
63
+ Unidecode==1.1.1
64
+ urllib3==1.25.7
65
+ Werkzeug==1.0.1
66
+ wget==3.2
67
+ wrapt==1.12.1
68
+ zope.event==4.4
69
+ zope.interface==5.1.0
server.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy as np
3
+ import tensorflow as tf
4
+ from module import encoder, decoder
5
+ from glob import glob
6
+ import runway
7
+
8
+
9
+ @runway.setup(options={"styleCheckpoint": runway.file(is_directory=True)})
10
+ def setup(opts):
11
+ sess = tf.Session()
12
+ init_op = tf.global_variables_initializer()
13
+ sess.run(init_op)
14
+ with tf.name_scope("placeholder"):
15
+ input_photo = tf.placeholder(
16
+ dtype=tf.float32, shape=[1, None, None, 3], name="photo"
17
+ )
18
+ input_photo_features = encoder(
19
+ image=input_photo, options={"gf_dim": 32}, reuse=False
20
+ )
21
+ output_photo = decoder(
22
+ features=input_photo_features, options={"gf_dim": 32}, reuse=False
23
+ )
24
+ saver = tf.train.Saver()
25
+ path = opts["styleCheckpoint"]
26
+ model_name = [p for p in os.listdir(path) if os.path.isdir(os.path.join(path, p))][
27
+ 0
28
+ ]
29
+ checkpoint_dir = os.path.join(path, model_name, "checkpoint_long")
30
+ ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
31
+ ckpt_name = os.path.basename(ckpt.model_checkpoint_path)
32
+ saver.restore(sess, os.path.join(checkpoint_dir, ckpt_name))
33
+ return dict(sess=sess, input_photo=input_photo, output_photo=output_photo)
34
+
35
+
36
+ @runway.command(
37
+ "stylize",
38
+ inputs={"contentImage": runway.image},
39
+ outputs={"stylizedImage": runway.image},
40
+ )
41
+ def stylize(model, inp):
42
+ img = inp["contentImage"]
43
+ img = np.array(img)
44
+ img = img / 127.5 - 1.0
45
+ img = np.expand_dims(img, axis=0)
46
+ img = model["sess"].run(
47
+ model["output_photo"], feed_dict={model["input_photo"]: img}
48
+ )
49
+ img = (img + 1.0) * 127.5
50
+ img = img.astype("uint8")
51
+ img = img[0]
52
+ return dict(stylizedImage=img)
53
+
54
+
55
+ if __name__ == "__main__":
56
+ #print("External Service port is:" + os.environ.get("SPORT",7860))
57
+ #set env var: RW_PORT=7860
58
+ os.environ["RW_PORT"] = "7860"
59
+ runway.run()