GetResizedFile.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import re
  2. from os import path, getcwd
  3. import cv2
  4. from PIL import Image
  5. from eventlet import tpool
  6. from flask import abort, send_from_directory
  7. from flask.views import View
  8. from pycs.database.Database import Database
  9. class GetResizedFile(View):
  10. """
  11. returns binary file after resizing
  12. """
  13. # pylint: disable=arguments-differ
  14. methods = ['GET']
  15. def __init__(self, db: Database):
  16. # pylint: disable=invalid-name
  17. self.db = db
  18. def dispatch_request(self, file_id: int, resolution: str):
  19. # get file from database
  20. file = self.db.file(file_id)
  21. if file is None:
  22. return abort(404, "file object not found")
  23. if not path.exists(file.path):
  24. return abort(404, "image not found!")
  25. project = file.project
  26. # extract desired resolution
  27. resolution = re.split(r'[^0-9]', resolution)
  28. max_width = int(resolution[0])
  29. max_height = int(resolution[1]) if len(resolution) > 1 else 2 ** 24
  30. # send data
  31. file_directory, file_name = tpool.execute(self.resize_file,
  32. project, file, max_width, max_height)
  33. return send_from_directory(file_directory, file_name)
  34. @staticmethod
  35. def resize_file(project, file, max_width, max_height):
  36. """
  37. If file type equals video this function extracts a thumbnail first. It calls resize_image
  38. to resize and returns the resized files directory and name.
  39. :param project: associated project
  40. :param file: file object
  41. :param max_width: maximum image or thumbnail width
  42. :param max_height: maximum image or thumbnail height
  43. :return: resized file directory, resized file name
  44. """
  45. # get absolute path
  46. if path.isabs(file.path):
  47. abs_file_path = file.path
  48. else:
  49. abs_file_path = path.join(getcwd(), file.path)
  50. # extract video thumbnail
  51. if file.type == 'video':
  52. abs_target_path = path.join(getcwd(), project.root_folder, 'temp', f'{file.uuid}.jpg')
  53. GetResizedFile.create_thumbnail(abs_file_path, abs_target_path)
  54. abs_file_path = abs_target_path
  55. # resize image file
  56. abs_target_path = path.join(getcwd(), project.root_folder,
  57. 'temp', f'{file.uuid}_{max_width}_{max_height}.jpg')
  58. result = GetResizedFile.resize_image(abs_file_path, abs_target_path, max_width, max_height)
  59. # return path
  60. if result is not None:
  61. return path.split(abs_target_path)
  62. return path.split(abs_file_path)
  63. @staticmethod
  64. def resize_image(file_path, target_path, max_width, max_height):
  65. """
  66. resize an image so width < max_width and height < max_height
  67. :param file_path: path to source file
  68. :param target_path: path to target file
  69. :param max_width: maximum image width
  70. :param max_height: maximum image height
  71. :return:
  72. """
  73. # return if file exists
  74. if path.exists(target_path):
  75. return True
  76. # load full size image
  77. image = Image.open(file_path)
  78. img_width, img_height = image.size
  79. # abort if file is smaller than desired
  80. if img_width < max_width and img_height < max_height:
  81. return None
  82. # calculate target size
  83. target_width = int(max_width)
  84. target_height = int(max_width * img_height / img_width)
  85. if target_height > max_height:
  86. target_height = int(max_height)
  87. target_width = int(max_height * img_width / img_height)
  88. # resize image
  89. resized_image = image.resize((target_width, target_height))
  90. # save to file
  91. resized_image.save(target_path, quality=80)
  92. return True
  93. @staticmethod
  94. def create_thumbnail(file_path, target_path):
  95. """
  96. extract a thumbnail from a video
  97. :param file_path: path to source file
  98. :param target_path: path to target file
  99. :return:
  100. """
  101. # return if file exists
  102. if path.exists(target_path):
  103. return
  104. # load video
  105. video = cv2.VideoCapture(file_path)
  106. # create thumbnail
  107. _, image = video.read()
  108. cv2.imwrite(target_path, image)
  109. # close video file
  110. video.release()