SliceGenerator.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import re
  2. import time
  3. import math
  4. import sys
  5. from threading import Thread
  6. from multiprocessing import Process, cpu_count, Queue
  7. from django.conf import settings
  8. from volumes.models import Volume, Texture, TextureInfo, RawFrame
  9. from volumes.processing.models import AutomaticProcessing
  10. from skimage import img_as_ubyte, img_as_int
  11. import numpy as np
  12. from imageprocessing import tiffPreparer, sliceMapCreator, binaryPreparer
  13. from . import MultiProcessUByte, Normalizer
  14. import logging
  15. import pdb
  16. logger = logging.getLogger(__name__)
  17. def generateListOfVolumes(volumesToProcess):
  18. automaticProcessing = AutomaticProcessing()
  19. automaticProcessing.finished = False
  20. automaticProcessing = automaticProcessing.save()
  21. for volumeId in volumesToProcess:
  22. AutomaticProcessing.objects(id=automaticProcessing.id).update_one(inc__processed = 1)
  23. Volume.objects(id=volumeId).update_one(set__generateSlicesStatus = 'running')
  24. volume = Volume.objects(id=volumeId).first()
  25. sliceGeneratorPlain({}, volume.id)
  26. if 'successful' in Volume.objects(id=volume.id).first().generateSlicesStatus:
  27. AutomaticProcessing.objects(id=automaticProcessing.id).update_one(inc__succeeded = 1)
  28. else:
  29. AutomaticProcessing.objects(id=automaticProcessing.id).update_one(inc__errored = 1)
  30. AutomaticProcessing.objects(id=automaticProcessing.id).update_one(push__erroredPaths = volume.path)
  31. def sliceGenerator(request, volumeId):
  32. sliceGeneratorPlain(request.POST, volumeId)
  33. def sliceGeneratorPlain(data, volumeId):
  34. '''
  35. This file generates the needed information and images to
  36. render a volume.
  37. For that, the volume has to be located in the local filesystem.
  38. The workflow to preprocess volume data is as follows:
  39. 1. read volume data from filesystem
  40. 2. make each slice in the volume to be square by cutting
  41. the edges
  42. 3. if float image, normalize it, and covert it to int16
  43. 4. save this data as rawframes to volume, to access it later
  44. 5. convert image to ubyte for first preview slice maps
  45. 6. generate slice maps
  46. 7. save slice maps to volume
  47. 8. save json information to volume
  48. '''
  49. sliceFrom = ''
  50. sliceTo = ''
  51. imgFormat = ''
  52. if 'sliceFrom' in data:
  53. sliceFrom = data['sliceFrom']
  54. if 'sliceTo' in data:
  55. sliceTo = data['sliceTo']
  56. if 'imgFormat' in data:
  57. imgFormat = data['imgFormat']
  58. size = None
  59. fileChanged = 'fileChanged' in data
  60. volume = Volume.objects(id=volumeId)[0]
  61. frames = []
  62. try:
  63. if volume.imageSequence is True:
  64. logger.debug('volId: %s, reading imagesequence' % (volumeId))
  65. frames = tiffPreparer.prepareImageSequence(volume.path)
  66. elif re.search(r'.tif(f|)$', volume.path):
  67. logger.debug('volId: %s, reading tiff file' % (volumeId))
  68. frames = tiffPreparer.processTiffFile(volume.path)
  69. elif volume.rawData:
  70. logger.debug('volId: %s, reading raw data' % (volumeId))
  71. frames = binaryPreparer.processBinary(volume.path, (volume.width, volume.height), littleEndian = volume.littleEndian)
  72. else:
  73. raise ValueError('sorry, file currently not supported, is it a raw file?')
  74. except Exception as e:
  75. logger.error('volId: %s, exception: %s)' % (volumeId, str(e)))
  76. volume.generateSlicesStatus = str(e)
  77. volume.save()
  78. return
  79. try:
  80. sliceFrom, sliceTo = __defineSliceRange(frames, sliceFrom, sliceTo)
  81. except Exception:
  82. logger.error('volId: %s, please provide integers or "" for "sliceFrom" and "sliceTo"' % (volumeId))
  83. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = 'generation failed, please read the logs')
  84. return
  85. if len(frames.shape) is not 4:
  86. raise ValueError('read input file shall have dim of 4, please check the used fileparser')
  87. if frames.dtype == np.float32 or frames.dtype == np.float16:
  88. try:
  89. Normalizer.normalize_image(frames, volumeId)
  90. except Exception as e:
  91. logger.error('volId: %s, error while normalization: ' % (str(e)))
  92. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = 'generation failed, please read the logs')
  93. return
  94. else:
  95. logger.debug('volId: %s, frames do NOT have to be normalized, dtype is %s' % (volumeId, str(frames.dtype)))
  96. logger.debug('volId: %s, cropping images to be square' % volumeId)
  97. oldshape = frames.shape
  98. pixelOffset, newSize = sliceMapCreator.calculateXYDimensions(frames[0])
  99. croppedFrames = np.empty((frames.shape[0], frames.shape[1], newSize[1], newSize[0]), dtype=frames.dtype)
  100. for frameNumber in range(0, frames.shape[0]):
  101. croppedFrames[frameNumber] = sliceMapCreator.cropFrame(frames[frameNumber], pixelOffset, newSize)
  102. del frames
  103. logger.debug('volId: %s, memory freed, frames deleted, keeping squared croppedFrames' % volumeId)
  104. if croppedFrames.dtype != np.int16 and croppedFrames.dtype != np.uint8:
  105. logger.debug('volId: %s, frames are not int16 and not uint8, convert them to int' % volumeId)
  106. croppedFrames = img_as_int(croppedFrames)
  107. else:
  108. logger.debug('volId: %s, frames are not converted because of dtype %s' % (volumeId, str(croppedFrames.dtype)))
  109. rawFrame = None
  110. # the file on disk has changed, we have to save it in the database
  111. if fileChanged or len(volume.rawFrames) == 0:
  112. if settings.REDUCE_HARDDISK_ACCESS_FOR_DEV is False:
  113. logger.debug('volId: %s, deleting rawframes from volume' % volumeId)
  114. volume.remove_frames()
  115. else:
  116. logger.debug('volId: %s, not deleting rawframes from volume --> reducing harddisk access' % volumeId)
  117. logger.debug('volId: %s, setting rawframes' % volumeId)
  118. try:
  119. for frameNumber in range(0, len(croppedFrames)):
  120. logger.debug('volId: %s, rawFrame: %d, sizeInMemory: %d' % (volumeId, frameNumber, sys.getsizeof(croppedFrames[frameNumber])))
  121. rawFrame = volume.add_frame(croppedFrames[frameNumber], frameNumber)
  122. except Exception:
  123. logger.error('volId: %s, please provide integers or "" for "sliceFrom" and "sliceTo"' % (volumeId))
  124. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = 'generation failed, please read the logs')
  125. return
  126. else:
  127. rawFrame = volume.rawFrames[0]
  128. logger.debug('volId: %s, rawframes have been appended to volume' % volumeId)
  129. try:
  130. logger.debug('volId: %s, convert images to ubyte for generating preview textures' % volumeId)
  131. framesUByte = MultiProcessUByte.multiprocess_img_as_ubyte(croppedFrames)
  132. del croppedFrames
  133. logger.debug('volId: %s, memory freed, croppedFrames deleted, keeping ubyte frames' % volumeId)
  134. except Exception as e:
  135. logger.error('volId: %s, exception: %s' % (volumeId, str(e)))
  136. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = str(e))
  137. return
  138. logger.debug('volId: %s, converting ended' % volumeId)
  139. originalSize = rawFrame.originalSize
  140. # free more memory
  141. logger.debug('volId: %s, del volume object now from RAM, free memory' % volumeId)
  142. del volume
  143. volume = Volume.objects(id=volumeId)[0]
  144. try:
  145. logger.debug('volId: %s, processing the image now' % volumeId)
  146. sprites = sliceMapCreator.processFrames(framesUByte, 0, len(framesUByte - 1), sliceFrom, sliceTo, imgFormat, size)
  147. infoObject = sliceMapCreator.generateInfo(sprites, 0, len(framesUByte) - 1, sliceFrom, sliceTo)
  148. except Exception as e:
  149. logger.error('volId: %s, exception: %s' % (volumeId, str(e)))
  150. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = str(e))
  151. return
  152. try:
  153. volume.clear_textures()
  154. except Exception as e:
  155. logger.error('volId: %s, exception: %s' % (volumeId, str(e)))
  156. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = str(e))
  157. return
  158. try:
  159. logger.debug('volId: %s, save sprites in volume' % volumeId)
  160. for key in sprites:
  161. volume.add_slice_maps(int(key), sprites[key], originalSize, infoObject)
  162. except Exception as e:
  163. logger.error('volId: %s, exception: %s' % (volumeId, str(e)))
  164. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = str(e))
  165. return
  166. Volume.objects(id=volume.id).update_one(set__generateSlicesStatus = 'generation successful')
  167. logger.debug('volId: %s, generation successful' % volumeId)
  168. '''
  169. This method returns the range of slices, that
  170. shall be generated
  171. '''
  172. def __defineSliceRange(frames, sliceFrom, sliceTo):
  173. if sliceFrom != '':
  174. sliceFrom = int(sliceFrom)
  175. else:
  176. sliceFrom = 0
  177. if sliceTo != '':
  178. sliceTo = int(sliceTo)
  179. else:
  180. dim = len(frames.shape)
  181. # accessing shape in z dim
  182. sliceTo = frames.shape[dim - 3] - 1
  183. return (sliceFrom, sliceTo)