File: testResynth.py

package info (click to toggle)
gimp-plugin-registry 9.20200928
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 52,524 kB
  • sloc: ansic: 27,119; lisp: 20,560; python: 14,721; cpp: 2,528; sh: 2,220; makefile: 635; xml: 21
file content (406 lines) | stat: -rw-r--r-- 15,206 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

'''
A script to test the resynthesizer.

Lloyd Konneker

Suite of several test cases.
Most test cases are the plugins that call the resynthesizer engine plugin.
A few are direct calls to the resynthesizer engine.
Note the plugins must be installed, this calls them through the pdb.

Uses diff to compare output image with known good results (reference images).
Output is to a temp directory ( typically /tmp).

Result type enumeration:
FAILED: output does not match reference image
SUCCESS: does match
UNKNOWN: no reference image exists, one was created, manually judge the reference and run testsuite again
EXCEPTION: a called plugin threw a RuntimeError exception
IMPROPER: a test file could not be opened, or similar

Reference images are created on the first run.
Delete a reference image and run testsuite again to create a new reference image.
OR review the output in temp and if OK, paste it over the reference image.

Note these call a version of the resynthesizer that is repeatable especially its random seed.
Note that anytime you change the code 
so that the prng pseudo random number generator sequence is changed
this automated test will usually fail but the functional result will be as expected.

Invoke:
cd resynthesizer/Test
gimp -i --batch-interpreter python-fu-eval --batch - < testResynth.py

It takes tens of minutes.  
Then review the log, where a summary is printed last.  Everything should pass.

Note save PPM because PPM format is large but repeatable,
whereas PNG is not repeatable because of timestamp.

Note that the test harness creates a new image from a file and 
saves the result separately from the input.

Note uses the logging module, mainly to avoid issues with print to stdout on some platforms.
If you want to see the results in real time, use 'tail -f resynth-test.log'
'''


from gimpfu import *
from commands import getstatusoutput
import os
import time
import tempfile
import logging


# Set up file paths
testdir = './'

# put test results in a tmp directory (typically in /tmp)
tmpdir = tempfile.mkdtemp()

indir = testdir + 'in_images/'
# For testing pure GIMP/glib with GRand: reference files different
referencedir = testdir + 'reference_out_images/'
# For testing with stdc rand(): 
## referencedir = testdir + 'reference_out_images_rand/'

test_summary = ""

# selection rects
select1 = (100, 90, 100, 50)  # ufo
select2 = (90, 175, 135, 100) # donkey

def drawable_of_file(filename):
  '''
  Load a file and return the the drawable.
  A call to this can be used as an actual parameter, see instance below.
  '''
  image = pdb.gimp_file_load(filename, filename, run_mode=RUN_NONINTERACTIVE)
  pdb.gimp_image_flatten(image)
  drawable = pdb.gimp_image_get_active_layer(image)
  return drawable

def drawable_of_file_with_anti_selection(filename, select):
  '''
  Load a file and return the the drawable.
  A call to this can be used as an actual parameter, see instance below.
  '''
  image = pdb.gimp_file_load(filename, filename, run_mode=RUN_NONINTERACTIVE)
  pdb.gimp_image_flatten(image)
  pdb.gimp_rect_select(image, select[0], select[1], select[2], select[3], 0, False, 0)
  pdb.gimp_selection_invert(image)
  drawable = pdb.gimp_image_get_active_layer(image)
  return drawable

def record_test_result(testname, result, outfilename = ""):
  global test_summary
  logging.info(result + " " + testname)
  test_summary += result + " " + testname + " " + outfilename + "\n"
  

def runtest(filename, testname, testcommand, testparameters, select=None):
  
  global test_summary
  
  logging.info("Running test " + testname)
  
  infilepath = indir + filename + '.png'
  # since some tests use the same input file, cat the testname to make an outfilename
  outfilename = testname + '-' + filename
  # out and reference have same name, different directories
  outfilepath = tmpdir + '/' + outfilename + '.ppm'
  referencefilepath = referencedir + '/' + outfilename + '.ppm'
  
  # open test file 
  try:
    image = pdb.gimp_file_load(infilepath, infilepath, run_mode=RUN_NONINTERACTIVE)
    drawable = pdb.gimp_image_get_active_layer(image)
    if select is not None:
      # Make selection    x,y, width, height
      # pdb.gimp_rect_select(image, 100, 90, 100, 50, 0, False, 0)
      pdb.gimp_rect_select(image, select[0], select[1], select[2], select[3], 0, False, 0)
  except:
    record_test_result(testname, "IMPROPER preprocessing")
    return
  
  # Build a test string
  teststring = testcommand + "(image, drawable," + testparameters + ",run_mode=RUN_NONINTERACTIVE)"
  logging.info("Test string:" + teststring)
  
  # Invoke the test
  # Formerly: eval(teststring) but eval only takes expressions, not statements
  start = time.time()
  try:
    exec teststring
  except RuntimeError:
    record_test_result(testname, "EXCEPTION")
    return
  logging.info("Elapsed time: " + str(time.time() - start))
  
  '''
  !!! Note that the test string can assign to image if the test returns a new image
  (rather than alter the image.)  Special handling here.
  '''
  logging.info("Test image name" + image.name)
  
  # test post processing
  try:
    # !!! Refresh drawable in case the plugin returned a new image
    drawable = pdb.gimp_image_get_active_drawable(image)
    
    # Save altered or new image in a temp directory.
    # !!! Why do you need to pass drawable, doesn't it save all the layers?  This bit me.
    if pdb.gimp_drawable_has_alpha(drawable) :
      pdb.gimp_image_flatten(image)   # Since saving ppm, get rid of alpha
      drawable = pdb.gimp_image_get_active_drawable(image)
    pdb.gimp_file_save(image, drawable, outfilepath, outfilepath, run_mode=RUN_NONINTERACTIVE)
    pdb.gimp_image_delete(image)
  except:
    record_test_result(testname, "IMPROPER post processing")
    return
  
  # If not reference out exists, make it (the first test run)
  if not os.path.exists(referencefilepath) :
    logging.debug("Making reference output image file.")
    status, output = getstatusoutput('cp ' + outfilepath + ' ' + referencefilepath)
    record_test_result(testname, "UNKNOWN ", outfilename)
    # test inconclusive, no reference data, must rerun
  else: 
    # diff reference image with recent image out
    status, output = getstatusoutput('diff ' + outfilepath + ' ' + referencefilepath)
  
    if status:  # !!! logic is reversed
      record_test_result(testname, "FAILED", outfilename)
    else:
      record_test_result(testname, "PASSED", outfilename)


def main():

  '''
  Run test cases for resynthesizer and plugins that call it.
  
  Notes on the test string:  optionally use names "image" and "drawable" to match the variable names used
  in the runtest() function; the names will be eval'ed.
  '''
  
  # Open the log
  LOG_FILENAME = 'resynth-test.log'
  logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
	
  logging.info("Temp directory: " + tmpdir)
  
  # Make paths to various input files
  grasspath = indir + 'grass-input-alpha.png'
  brickpath = indir + 'brick.png'
  donkeypath = indir + 'donkey_original.png'
  angelpath = indir + 'angel_texture.png'
  wanderpath = indir + 'wander-texture.png'
  zappath = indir + 'zap-texture.png'
  ufopath = indir + 'ufo-input.png'
  
  
  # Begin test definitions
  
  '''
  Straight resynthesis tests (without using a helper plugin, just resynth)
  '''
  
  # Resynthesis of small file from full context
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "0,0, True, drawable_of_file_with_anti_selection('"+ zappath + "', select1).ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('zap-texture', 'resynthfull', test, parameters, select1)
  
  # Resynthesis of ufo from full context
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "0,0, True, drawable_of_file_with_anti_selection('"+ ufopath + "', select1).ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('ufo-input', 'resynthfull', test, parameters, select1)
  
  # Resynthesis of rect selection in bricks from full context
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "0,0, True, drawable_of_file_with_anti_selection('"+ brickpath + "', select1).ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('brick', 'resynthfull', test, parameters, select1)
  
  # Resynthesis of rect selection in donkey from full context
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "0,0, True, drawable_of_file_with_anti_selection('"+ donkeypath + "', select2).ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('donkey_original', 'resynthfull', test, parameters, select2)
 
  '''
  Tests of mapping using helper plugins.
  '''
  # Texture transfer (map style) with texture having alpha
  # map style: RGBA to RGB w/ color maps
  
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ grasspath + "'), 50.0, 0"  
  # source image, fifty percent transfer, map by colors
  runtest('ufo-input', 'mapstylealpha', test, parameters, select=None)
  
  # Texture transfer (map style) with target having alpha
  # map style: RGB to RGBA w/ color maps
  # TODO this is not a test of the resynth since the plug-in is adding an alpha to texture
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ grasspath + "'), 50.0, 0"  
  # source image, fifty percent transfer, map by colors
  runtest('ufo-input-w-alpha', 'mapstyletoalpha', test, parameters, select=None)
  
  # Texture transfer (map style)
  # map style: RGB to RGB w/ color maps
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ grasspath + "'), 50.0, 0"  
  # source image, fifty percent transfer, map by colors
  runtest('ufo-input', 'mapstyle', test, parameters, select=None)
  
  # map style: RGB to RGB w/ color maps
  # This is conceptually little different from the above test, but it is a popular example.
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ angelpath + "'), 10.0, 0"  
  # source image, fifty percent transfer, map by colors
  runtest('angel_target', 'mapstyleangel', test, parameters, select=None)
  
  # map style: RGB to RGB w/ GRAY maps
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ grasspath + "'), 50.0, 1"  
  # source image, fifty percent transfer, map by brightness
  runtest('ufo-input', 'mapstylebrightness', test, parameters, select=None)
  
  # mapstyle: GRAY to GRAY
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ wanderpath + "'), 50.0, 0"  
  # source image, fifty percent transfer, map by colors
  runtest('wander', 'mapstylegraygray', test, parameters, select=None)
  
  # mapstyle: RGB to GRAY
  test = "pdb.python_fu_map_style"
  parameters = "drawable_of_file('"+ zappath + "'), 10.0, 0"  
  # source image, ten percent transfer, map by colors
  runtest('zap-output-map', 'mapstylergbgray', test, parameters, select=None)
  
  # Render texture
  # !!! Note here the plugin returns a new image which we assign to image variable
  test = "image = pdb.python_fu_render_texture"
  parameters = "2, 1"
  runtest('grass-input', 'rendertexture', test, parameters, select=None)
  
  '''
  More straight resynthesis tests.
  '''
  
  # Straight resynthesis, from selection in drawable into same selection in drawable
  # !!! Note the target image is not in this parameter string, it is the corpus.
  test = "pdb.plug_in_resynthesizer"
  parameters = "0,0, True, drawable.ID, -1, -1, 0.0, 0.117, 16, 500"
  runtest('ufo-input', 'resynth', test, parameters, select1)
  
  # Straight resynthesis, from separate image with no selection into selection in drawable
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "0,0, True, drawable_of_file('"+ grasspath + "').ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('ufo-input', 'resynthtwoimages', test, parameters, select1)
  
  # Straight resynthesis, from separate image with no selection into selection in drawable
  # also, tileable without using context: 1,1,0,...
  test = "pdb.plug_in_resynthesizer"
  # !!! Note we want the ID of the drawable
  parameters = "1, 1, 0, drawable_of_file('"+ grasspath + "').ID, -1, -1, 0.0, 0.117, 16, 500" 
  runtest('ufo-input', 'resynthtileable', test, parameters, select1)
  
  '''
  Heal selection plugin tests.
  '''
  # Heal from donut corpus
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 1, 1" # 1 = sample from sides, 1 = direction inward
  runtest('ufo-input', 'heal', test, parameters, select1)
  
  # Heal from donut corpus
  # also randomly all around
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 0, 0" # 0 = sample from all around, 0 = randomly
  runtest('ufo-input', 'healaroundrandom', test, parameters, select1)
  
  # Heal where target includes alpha layer.
  # This selects from an area having a transparent area.
  # The transparent area should NOT be resynthesized, but yield white upon flattening.
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 1, 1" 
  runtest('ufo-input-w-alpha', 'healincludedalpha', test, parameters, select1)
  
  # Heal where target has distant alpha layer.
  # This selects from a black sky area with a nearby transparent area.
  # The result should be black resynthesized, NOT white of transparent flattened.
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 1, 1"
  runtest('apollo11_w_alpha', 'healalpha', test, parameters, select1)
  
  # Heal a grayscale
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 1, 1"
  runtest('wander', 'healgray', test, parameters, select1)
  
  # Heal a grayscale w alpha
  test = "pdb.python_fu_heal_selection"
  parameters = "50, 0, 1"
  runtest('ufo-input-w-alpha-gray', 'healalphagray', test, parameters, select1)
  
  # Heal transparency outward
  test = "pdb.python_fu_heal_transparency"
  parameters = "50, 2" # pixels of context width, outward
  runtest('apollo11_w_alpha', 'healtransparency', test, parameters, select=None)
  
  # Heal transparency inward
  test = "pdb.python_fu_heal_transparency"
  parameters = "50, 1" # pixels of context width, 1=inward
  runtest('apollo11_w_alpha', 'healtransparencyinward', test, parameters, select=None)
  
  '''
  Other plugin tests
  '''
  
  # Sharpen with resynthesis
  ''' Too slow to test regularly '''
  '''
  test = "pdb.python_fu_sharpen_resynthesized"
  parameters = "2"
  runtest('ufo-input', 'sharpen', test, parameters, select=None)
  '''
  
  # Uncrop 20%
  test = "pdb.python_fu_uncrop"
  parameters = "20"
  runtest('ufo-input', 'uncrop', test, parameters, select=None)

  # Fill resynthesized pattern
  test = "pdb.python_fu_fill_pattern_resynth"
  parameters = " 'Maple Leaves' "
  runtest('ufo-input', 'fillpattern', test, parameters, select1)

  # Enlarge with resynthesis
  # Enlarge factor = 2
  # !!! Very slow
  ''' Too slow to test regularly '''
  '''
  test = "pdb.python_fu_enlarge_resynthesized"
  parameters = "2"
  runtest('ufo-input-small', 'enlarge', test, parameters, select=None)
  '''
  
  
 
  
  
  # TODO a much harder test with many layers and alpha channels

  logging.info("\nTemp directory: " + tmpdir)
  logging.info( "\n" + test_summary)
  
  pdb.gimp_quit(True)
  
main()