Browse Source

Optimize & enhance animation writing

Now we can specify a printf filename format and the save is made by PIL.
The image is build from array in main thread (avoid overhead of sending the
array through a pipe) and then saved by a Pool.
Yann Weber 6 years ago
parent
commit
723bba7d96
1 changed files with 66 additions and 31 deletions
  1. 66
    31
      gte/__main__.py

+ 66
- 31
gte/__main__.py View File

@@ -7,7 +7,8 @@ import time
7 7
 import datetime
8 8
 import numpy as np
9 9
 from random import randint
10
-from multiprocessing import Pool, Process, Queue
10
+import multiprocessing
11
+from multiprocessing import Pool, Process, Pipe
11 12
 
12 13
 logging.basicConfig(level=logging.INFO,
13 14
                     format="%(created)d %(asctime)s:%(module)s(%(lineno)d):%(levelname)s:%(message)s")
@@ -91,6 +92,8 @@ parser_gen.add_argument('--output-dir', '-A', type=str, default=None,
91 92
                         help="Save images for animation in this directory")
92 93
 parser_gen.add_argument('--anim-div', '-D', type=int, default=2,
93 94
                         help='Save all X images (default 2) when -O given')
95
+parser_gen.add_argument('--anim-fmt', '-F', type=str, default='GTE_ANIM_%d.png',
96
+                        help='Save all X images (default "GTE_ANIM_%d.png") when -O given')
94 97
 parser_gen.add_argument('--world-height', '-y', type=int, metavar='HEIGHT',
95 98
                         default=512)
96 99
 parser_gen.add_argument('--world-width', '-x', type=int, metavar='WIDTH',
@@ -104,7 +107,8 @@ parser_gen.add_argument('--gray', '-G', action='store_const',
104 107
 parser_gen.add_argument('--no-diagonals', '-d', action='store_const',
105 108
                         default=False, const=True)
106 109
 parser_gen.add_argument('--threads', '-t', type=int, metavar='N',
107
-                        default=2, help="Animation writting threads")
110
+                        default=os.cpu_count()-1,
111
+                        help="Animation writting threads")
108 112
 
109 113
 args = parser.parse_args()
110 114
 
@@ -223,23 +227,16 @@ if 'pool_size' in args:
223 227
 else:
224 228
     # Generate
225 229
     if args.output_dir is not None:
226
-        #from PIL import Image
227
-        import scipy
228
-        def save_anim(data_q):
229
-            while True:
230
-                wval, fname = data_q.get()
231
-                if wval is None:
232
-                    return
233
-                #im = Image.fromarray(wval, 'RGB')
234
-                #im.save(fname)
235
-                scipy.misc.imsave(fname, wval)
236
-                logger.info('Written %s / %d' % (fname, args.steps//args.anim_div))
237
-
238
-        data_q = Queue()
239
-        wps = [Process(target=save_anim, args=(data_q,))
240
-               for _ in range(args.threads)]
241
-        for wp in wps:
242
-            wp.start()
230
+        from PIL import Image
231
+        def save_pool(im, fname):
232
+            st1 = time.time()
233
+            im.save(fname)
234
+            logger.debug('Image %s saved in %.3fs' % (fname, 
235
+                                                      time.time() - st1))
236
+
237
+        write_pool = Pool(args.threads)
238
+        async_res = []
239
+        img_count = 0
243 240
     w = World(args.world_height, args.world_width, gray=args.gray)
244 241
     prog = rpnlib.RpnExpr.from_string(args.prog)
245 242
     turmits = [LivingTurmit(world=w, prog=prog, diag=not args.no_diagonals)
@@ -256,21 +253,59 @@ else:
256 253
             logger.info(msg)
257 254
         for turmit in turmits:
258 255
             turmit()
259
-        if args.output_dir is not None and step % args.anim_div == 0:
260
-            fname = 'GTE_ANIM_%d.png' % (step/args.anim_div)
256
+        if args.output_dir is not None and (args.anim_div <= 1 or
257
+                                            step % (args.anim_div-1) == 0):
258
+            imgid = step // (1 if args.anim_div <= 1 else args.anim_div)
259
+            img_count += 1
260
+            fname = args.anim_fmt % imgid
261 261
             fname = os.path.join(args.output_dir, fname)
262
-            data_q.put((w._val, fname))
263
-        #if args.output_dir is not None and step % args.anim_div == 0:
264
-        #    #w.save('/tmp/anim/GTE_ANIM_%d.png' % (step/10))
265
-        #    fname = 'GTE_ANIM_%d?png' % (step/args.anim_div)
266
-        #    w.save(os.path.join(args.output_dir, fname))
262
+            im = Image.fromarray(np.uint8(w._val))
263
+            async_res.append(write_pool.apply_async(save_pool, (im, fname)))
264
+            while async_res[0].ready():
265
+                async_res[0].get()
266
+                async_res.pop(0)
267 267
     
268 268
     if args.output_dir is not None:
269
-        for _ in wps:
270
-            data_q.put((None, None))
271
-        for wp in wps:
272
-            wp.join()
273
-        data_q.close()
269
+        while async_res[0].ready():
270
+            async_res[0].get()
271
+            sync_res.pop(0)
272
+        write_pool.close()
273
+        logger.info('Waiting for animation to be written on disk')
274
+        img_st1 = None
275
+        while len(async_res) > 0:
276
+            async_res[0].get()
277
+            async_res.pop(0)
278
+            stat_upd = 100
279
+            if len(async_res) % stat_upd == 0:
280
+                msg = '%d images left' % (len(async_res))
281
+                stupd = stat_upd
282
+                if img_st1 is None:
283
+                    img_st1 = start
284
+                    stupd = img_count - len(async_res)
285
+                img_st2 = time.time()
286
+                img_st = (img_st2 - img_st1) / stupd
287
+                img_ts = stupd / (img_st2 - img_st1)
288
+                etcs = ''
289
+                if img_st1 != start:
290
+                    etc = len(async_res) * img_st
291
+                    etcs = ' ETC:'
292
+                    if etc >= 60:
293
+                        etcs += '%d min ' % (etc // 60)
294
+                        etc %= 60
295
+                    etcs += '%ds' % etc
296
+                msg += ' %.1fimg/s%s' % (img_ts, etcs)
297
+                img_st1 = img_st2
298
+                logger.info(msg)
299
+                    
300
+
301
+
302
+        write_pool.join()
303
+        #for _ in wps:
304
+        #    data_q.put((None, None))
305
+        #for wp in wps:
306
+        #    wp.join()
307
+        #data_q.close()
308
+        logger.info('Animation written in "%s"' % args.output_dir)
274 309
     stop = time.time()
275 310
 
276 311
     score_dir = sum([t.dirvar() for t in turmits]) / len(turmits)

Loading…
Cancel
Save