Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

mark_stimulus.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. from view.python_core.utils.pil_helpers import add_string
  2. from PIL.ImageDraw import Draw
  3. from view.python_core.utils.colors import mpl_color_to_PIL
  4. from view.python_core.utils.fonts import get_maximum_font_size_by_width
  5. class NoStimulus(object):
  6. def __init__(self, pulsed_stimuli_handler):
  7. super().__init__()
  8. self.pulsed_stimuli_handler = pulsed_stimuli_handler
  9. self.font_size = None
  10. def get_frame_odor_conc(self, frame_time):
  11. [odors], [concs] = self.pulsed_stimuli_handler.get_odor_info_at_times([frame_time])
  12. return odors, concs
  13. def mark(self, img, frame_time):
  14. odors, concs = self.get_frame_odor_conc(frame_time)
  15. if len(odors) > 0 and len(concs) > 0:
  16. return self.add_mark_to_image(img, odors[0], concs[0])
  17. else:
  18. return img
  19. def add_mark_to_image(self, img, odor, conc):
  20. return img
  21. class SquareStimulus(NoStimulus):
  22. def __init__(self, mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler):
  23. super().__init__(pulsed_stimuli_handler)
  24. self.square_side = int(0.5 * mv_ygap)
  25. self.top_left_xy = (mv_xgap, int(0.25 * mv_ygap))
  26. self.fg_color = fg_color_for_pil
  27. def add_mark_to_image(self, img, odor, conc):
  28. img_draw_obj = Draw(img)
  29. img_draw_obj.rectangle(xy=(*self.top_left_xy,
  30. self.top_left_xy[0] + self.square_side,
  31. self.top_left_xy[1] + self.square_side),
  32. fill=self.fg_color,
  33. width=0)
  34. return img
  35. class SquareStimulusRed(SquareStimulus):
  36. def __init__(self, mv_xgap, mv_ygap, pulsed_stimuli_handler):
  37. super().__init__(mv_xgap=mv_xgap, mv_ygap=mv_ygap, pulsed_stimuli_handler=pulsed_stimuli_handler,
  38. fg_color_for_pil=mpl_color_to_PIL("r"))
  39. class BaseTextStimulus(NoStimulus):
  40. def __init__(self, mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler, font_file,
  41. frame_size):
  42. super().__init__(pulsed_stimuli_handler)
  43. self.mv_xgap = mv_xgap
  44. self.mv_ygap = mv_ygap
  45. self.fg_color_for_pil = fg_color_for_pil
  46. self.font_file = font_file
  47. all_odors = pulsed_stimuli_handler.stimulus_frame["Odor"]
  48. all_concs = pulsed_stimuli_handler.stimulus_frame["Concentration"]
  49. stimulus_strings = [self.compose_stim_text(odor, conc) for odor, conc in zip(all_odors, all_concs)]
  50. stimulus_string_lengths = [len(x) for x in stimulus_strings]
  51. longest_stimulus_string = stimulus_strings[stimulus_string_lengths.index(max(stimulus_string_lengths))]
  52. self.font_size = get_maximum_font_size_by_width(
  53. maximum_width=frame_size[0], text=longest_stimulus_string, font_name=font_file)
  54. def add_mark_to_image(self, img, odor, conc):
  55. stim_y_pos = self.mv_ygap * 0.95
  56. stim_text = self.compose_stim_text(odor, conc)
  57. img_with_stim = add_string(img, text=stim_text,
  58. position=(self.mv_xgap, stim_y_pos),
  59. font_size=self.font_size, fill_color_for_pil=self.fg_color_for_pil,
  60. horizontal_alignment="left", vertical_alignment="bottom", font_file=self.font_file)
  61. return img_with_stim
  62. class OdorConcTextStimulus(BaseTextStimulus):
  63. def __init__(self, mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler, font_file,
  64. frame_size):
  65. super().__init__(
  66. mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler, font_file,frame_size)
  67. def compose_stim_text(self, odor, conc):
  68. return f"{odor}@{conc}"
  69. class OdorTextStimulus(BaseTextStimulus):
  70. def __init__(self, mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler, font_file,
  71. frame_size):
  72. super().__init__(
  73. mv_xgap, mv_ygap, fg_color_for_pil, pulsed_stimuli_handler, font_file,frame_size)
  74. def compose_stim_text(self, odor, conc):
  75. return f"{odor}"
  76. def get_stimulus_marker(mv_markStimulus, left_xgap, mv_ygap, color_for_pil, pulsed_stimuli_handler,
  77. font_file, frame_size):
  78. if mv_markStimulus == 0:
  79. return NoStimulus(pulsed_stimuli_handler=pulsed_stimuli_handler)
  80. elif mv_markStimulus == 1:
  81. return SquareStimulus(mv_xgap=left_xgap, mv_ygap=mv_ygap, fg_color_for_pil=color_for_pil,
  82. pulsed_stimuli_handler=pulsed_stimuli_handler)
  83. elif mv_markStimulus == 2:
  84. return OdorConcTextStimulus(
  85. mv_xgap=left_xgap, mv_ygap=mv_ygap, fg_color_for_pil=color_for_pil,
  86. pulsed_stimuli_handler=pulsed_stimuli_handler, font_file=font_file,
  87. frame_size=frame_size)
  88. elif mv_markStimulus == 21:
  89. return OdorTextStimulus(
  90. mv_xgap=left_xgap, mv_ygap=mv_ygap, fg_color_for_pil=color_for_pil,
  91. pulsed_stimuli_handler=pulsed_stimuli_handler, font_file=font_file,
  92. frame_size=frame_size)
  93. elif mv_markStimulus == 3:
  94. return SquareStimulusRed(mv_xgap=left_xgap, mv_ygap=mv_ygap, pulsed_stimuli_handler=pulsed_stimuli_handler)
  95. else:
  96. raise NotImplementedError