clamas

Join Ultra Fractal splitted images

Apr 14th, 2021
88
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python
  2.  
  3. # uf_pic_join.py
  4. #
  5. # Author:
  6. #      Carlos Lamas
  7. # Date:
  8. #      2021-04-14
  9. # Version:
  10. #      1.1
  11. # Copyright:
  12. #      This code is licensed as public domain and can be used
  13. #      without any restriction
  14. # Description:
  15. #      This script allows to join a set of splitted tiles of an
  16. #      Ultra Fractal rendered image in a single image. Ultra
  17. #      Fractal images size are limited to 2GiB so splitted images
  18. #      may be required to render bigger images
  19. # Requirements
  20. #      * libvips instaled and configured in system path
  21. #      * pyvips binding installed
  22. #      * python is required to run the script
  23. # Command syntax:
  24. #      python uf_pic_join.py <corner_pic_file> <destination_file>
  25. # Command example:
  26. #      python uf_pic_join.py 'my fractal E4.png' 'full fractal.png'
  27. # Notes:
  28. #      * <corner_pic_file> is the name of the bottom right corner
  29. #        tile (something like 'myfractal E4.png')
  30. #      * <destination_file> is the name of the file to contain the
  31. #        mosaic with all the picture parts joined. It has not to
  32. #        be the same file format as the source image parts
  33. #      * Tested only in Windows OS and Python 3. Some tweaking can
  34. #        be necessary to run it in another OS
  35. #      * Using older libvips version 8.9.2 due to some issues
  36. #        related to pyvips with current versions 8.10.x
  37. #
  38.  
  39.  
  40. import os, os.path
  41. import sys, time
  42. import logging
  43. import pyvips
  44.  
  45.  
  46.  
  47. # Set the following constants to configure the command execution
  48. LOGGING_LEVEL = logging.WARNING
  49. PIVIPS_CACHE_TRACE = False
  50. PIVIPS_REPORT_RAM_LEAKED = True
  51.  
  52.  
  53.  
  54. # Initialize eval handler last_time "static" variable
  55. def preeval_handler(image, progress):
  56.     eval_handler.last_time = 0
  57.     preeval_handler.start_time = time.time()
  58.     print('Joining images...')
  59.     print('Percent complete: {0}%'.format(progress.percent), end = "")
  60.  
  61.  
  62. # Eval handler used to track the progress of the operation
  63. # Print the percent of operation done (only once every second)
  64. def eval_handler(image, progress):
  65.     current_time = progress.run   # time counter
  66.     if current_time - eval_handler.last_time >= 1:
  67.         print('\rPercent complete: {0}%'.format(progress.percent), end = "")
  68.         eval_handler.last_time = current_time
  69.  
  70.  
  71. # Force printing the percent at the end
  72. def posteval_handler(image, progress):
  73.     print('\rPercent complete: {0}%'.format(progress.percent))
  74.     print('Total time spent: {0:.2f}s'.format(time.time() - preeval_handler.start_time))
  75.  
  76.  
  77. # Print the shell command syntax
  78. def print_command_syntax():
  79.     print("Command syntax: python {0} <last_mosaic_file> <destination_file>".format(sys.argv[0]))
  80.  
  81.  
  82. def main():
  83.  
  84.     # Check 2 arguments are passed to the command
  85.     if len(sys.argv) != 3:
  86.         print("Invalid number of arguments ({0}).".format(len(sys.argv) - 1))
  87.         print_command_syntax()
  88.         return -1
  89.  
  90.     # Source files (2nd command argument)
  91.     src_file_path, src_file_name = os.path.split(os.path.normpath(sys.argv[1].strip()))
  92.     src_file_name, src_file_ext = os.path.splitext(src_file_name)
  93.  
  94.     # Destination file (3rd command argument)
  95.     dst_file_path, dst_file_name = os.path.split(os.path.normpath(sys.argv[2].strip()))
  96.     dst_file_name, dst_file_ext = os.path.splitext(dst_file_name)
  97.  
  98.  
  99.     # Check source file names
  100.     if len(src_file_name) <= 3 or src_file_name[-3] != ' ':
  101.         print("Invalid source file: '{0}'".format(src_file_name))
  102.         print_command_syntax()
  103.         return -1
  104.  
  105.  
  106.     # The row is the last character and the column is the second last
  107.     rows = src_file_name[-1]           # digit 0..9
  108.     cols = src_file_name[-2].upper()   # character A..I
  109.  
  110.     rows = "123456789".find(rows) + 1   # 1..9
  111.     if rows == 0:
  112.         print("Invalid source file name. Invalid row coordinate")
  113.         return -1
  114.  
  115.     cols= "ABCDEFGHI".find(cols) + 1   # 1..9
  116.     if cols == 0:
  117.         print("Invalid source file name. Invalid column coordinate")
  118.         return -1
  119.  
  120.  
  121.     # pyvips configuration
  122.     pyvips.cache_set_trace(PIVIPS_CACHE_TRACE)
  123.     pyvips.base.leak_set(PIVIPS_REPORT_RAM_LEAKED) # show memory used
  124.  
  125.  
  126.     # Create image list with sorted source files
  127.     image_list = []
  128.     src_file_name = src_file_name[:-2]  # remove coordinates to get the base name
  129.     for r in range(rows):
  130.         for c in range(cols):
  131.             suffix = chr(ord('A') + c) + str(r + 1)
  132.             file_name = os.path.join(src_file_path, src_file_name + suffix + src_file_ext)
  133.             image = pyvips.Image.new_from_file(file_name, access='sequential')
  134.             image_list.append(image)
  135.  
  136.  
  137.     # Abort when destination file already exists
  138.     dst_file = os.path.join(dst_file_path, dst_file_name + dst_file_ext)
  139.     if os.path.exists(dst_file):
  140.         print('Invalid destination file. A file or folder with that name already exists.')
  141.         return -1
  142.  
  143.     dst_image = pyvips.Image.arrayjoin(image_list, across = cols)
  144.  
  145.  
  146.     # Configure tracking handlers
  147.     dst_image.set_progress(True)
  148.     dst_image.signal_connect('preeval', preeval_handler)
  149.     dst_image.signal_connect('eval', eval_handler)
  150.     dst_image.signal_connect('posteval', posteval_handler)
  151.  
  152.     dst_image.write_to_file(dst_file)
  153.  
  154.     return 0
  155.  
  156.  
  157. # Run main()
  158. if __name__ == "__main__":
  159.  
  160.     logging.basicConfig(level = LOGGING_LEVEL)
  161.     result = main()
  162.     exit(result)
  163.  
RAW Paste Data