From d0e8717cf04f167f16b4a07296ae01f99242fe43 Mon Sep 17 00:00:00 2001 From: Kay Robbins <1189050+VisLab@users.noreply.github.com> Date: Sun, 25 Feb 2024 06:42:01 -0600 Subject: [PATCH 1/2] Added EventManager raises exception when onsets unordered --- hed/tools/analysis/event_manager.py | 7 +++- hed/tools/visualization/tag_word_cloud.py | 3 +- tests/tools/analysis/test_event_manager.py | 38 +++++++++++++++++++ .../visualization/test_tag_word_cloud.py | 3 +- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/hed/tools/analysis/event_manager.py b/hed/tools/analysis/event_manager.py index 959398e6..160a4dea 100644 --- a/hed/tools/analysis/event_manager.py +++ b/hed/tools/analysis/event_manager.py @@ -1,5 +1,7 @@ """ Manager of events of temporal extent. """ +import pandas as pd +from hed.errors import HedFileError from hed.models import HedString from hed.models.model_constants import DefTagNames from hed.models.df_util import get_assembled @@ -31,7 +33,10 @@ def __init__(self, input_data, hed_schema, extra_defs=None): self.hed_schema = hed_schema self.input_data = input_data self.def_dict = input_data.get_def_dict(hed_schema, extra_def_dicts=extra_defs) - self.onsets = input_data.dataframe['onset'].tolist() + onsets = pd.to_numeric(input_data.dataframe['onset'], errors='coerce') + if not onsets.is_monotonic_increasing: + raise HedFileError("OnsetsNotOrdered", "The onset values must be non-decreasing", "") + self.onsets = onsets.tolist() self.hed_strings = None # Remaining HED strings copy.deepcopy(hed_strings) self._create_event_list(input_data) diff --git a/hed/tools/visualization/tag_word_cloud.py b/hed/tools/visualization/tag_word_cloud.py index 691e18dd..eaf8375d 100644 --- a/hed/tools/visualization/tag_word_cloud.py +++ b/hed/tools/visualization/tag_word_cloud.py @@ -45,7 +45,8 @@ def create_wordcloud(word_dict, mask_path=None, background_color=None, width=400 kwargs.setdefault('relative_scaling', 1) kwargs.setdefault('max_font_size', height / 20) kwargs.setdefault('min_font_size', 8) - if font_path and not font_path.endswith(".ttf") and not font_path.endswith(".otf"): + if font_path and not font_path.endswith(".ttf") and not font_path.endswith(".otf")\ + and not font_path.endswith(".TTF") and not font_path.endswith(".OTF"): font_path = fm.findfont(font_path) wc = WordCloud(font_path=font_path, background_color=background_color, mask=mask_image, diff --git a/tests/tools/analysis/test_event_manager.py b/tests/tools/analysis/test_event_manager.py index d221f2c4..ec364bac 100644 --- a/tests/tools/analysis/test_event_manager.py +++ b/tests/tools/analysis/test_event_manager.py @@ -1,6 +1,9 @@ import os import unittest +import numpy as np +import pandas as pd +from hed.errors import HedFileError from hed.models.sidecar import Sidecar, HedString from hed.models.tabular_input import TabularInput from hed.schema.hed_schema_io import load_schema_version @@ -77,6 +80,41 @@ def test_get_type_defs(self): self.assertIsInstance(def_names, list) self.assertEqual(11, len(def_names)) + def test_onset_ordering_mixed(self): + df = pd.DataFrame({'onset': [1, 2, '3', 3.24, 5], + 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'Green', 'Label/1']}) + manager = EventManager(TabularInput(df), self.schema) + self.assertIsInstance(manager, EventManager) + hed, base, context = manager.unfold_context() + print("to here") + # df = pd.DataFrame( + # {'onset': [1, 2, '3', np.nan, 5], 'HED': ['Sensory-event', 'Red', 'Blue', 'Green', 'Label/1']}) + # pass + + def test_onset_ordering_bad(self): + df = pd.DataFrame({'onset': [1, 2, '3', 'n/a', 5], + 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'n/a', 'Label/1']}) + with self.assertRaises(HedFileError) as ex: + EventManager(TabularInput(df), self.schema) + self.assertEqual(ex.args(0), "OnsetsNotOrdered") + df1 = pd.DataFrame({'onset': [1, 2, 1.4, 6, 5], + 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'n/a', 'Label/1']}) + with self.assertRaises(HedFileError) as ex1: + EventManager(TabularInput(df1), self.schema) + self.assertEqual(ex1.args(0), "OnsetsNotOrdered") + + def test_duration_context(self): + df = pd.DataFrame({'onset': [1, 2, 3, 4, 5], + 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'n/a', 'Label/1']}) + manager = EventManager(TabularInput(df), self.schema) + hed, base, context = manager.unfold_context() + pass + + + # df = pd.DataFrame( + # {'onset': [1, 2, '3', np.nan, 5], 'HED': ['Sensory-event', 'Red', 'Blue', 'Green', 'Label/1']}) + # pass + if __name__ == '__main__': unittest.main() diff --git a/tests/tools/visualization/test_tag_word_cloud.py b/tests/tools/visualization/test_tag_word_cloud.py index 1516f3b9..8bf16e27 100644 --- a/tests/tools/visualization/test_tag_word_cloud.py +++ b/tests/tools/visualization/test_tag_word_cloud.py @@ -43,7 +43,8 @@ def test_create_wordcloud_font_direct(self): fonts = fm.findSystemFonts() first_font = fonts[0] - + x = '/C/Windows/Fonts/timesi.ttf' + #y = 'C:\\Windows\\Fonts\\arialbd.ttf' wc = tag_word_cloud.create_wordcloud(word_dict, width=width, height=height, font_path=first_font) self.assertIsInstance(wc, wordcloud.WordCloud) From 0759e41111567241d7b9c004afcf88c13a3c6488 Mon Sep 17 00:00:00 2001 From: Kay Robbins <1189050+VisLab@users.noreply.github.com> Date: Sun, 25 Feb 2024 09:40:18 -0600 Subject: [PATCH 2/2] Incorporated font path into remodeling ops --- .../operations/summarize_hed_tags_op.py | 11 ++++++++++- hed/tools/visualization/tag_word_cloud.py | 14 ++++++++------ tests/tools/analysis/test_event_manager.py | 15 ++++++--------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hed/tools/remodeling/operations/summarize_hed_tags_op.py b/hed/tools/remodeling/operations/summarize_hed_tags_op.py index 8db7e1aa..36e19665 100644 --- a/hed/tools/remodeling/operations/summarize_hed_tags_op.py +++ b/hed/tools/remodeling/operations/summarize_hed_tags_op.py @@ -91,6 +91,12 @@ class SummarizeHedTagsOp(BaseOp): "max_font_size": { "type": "number" }, + "set_font": { + "type": "boolean" + }, + "font_path": { + "type": "string" + }, "scale_adjustment": { "type": "number" }, @@ -148,6 +154,8 @@ def __init__(self, parameters): "prefer_horizontal": wc_params.get("prefer_horizontal", 0.75), "min_font_size": wc_params.get("min_font_size", 8), "max_font_size": wc_params.get("max_font_size", 15), + "set_font": wc_params.get("set_font", False), + "font_path": wc_params.get("font_path", ""), "scale_adjustment": wc_params.get("scale_adjustment", 7), "contour_width": wc_params.get("contour_width", 3), "contour_color": wc_params.get("contour_color", 'black'), @@ -305,7 +313,8 @@ def save_visualizations(self, save_dir, file_formats=['.svg'], individual_summar tag_wc = create_wordcloud(word_dict, mask_path=wc["mask_path"], width=wc["width"], height=wc["height"], prefer_horizontal=wc["prefer_horizontal"], background_color=wc["background_color"], min_font_size=wc["min_font_size"], max_font_size=wc["max_font_size"], - contour_width=wc["contour_width"], contour_color=wc["contour_color"]) + contour_width=wc["contour_width"], contour_color=wc["contour_color"], + set_font=wc["set_font"], font_path=wc["font_path"]) svg_data = word_cloud_to_svg(tag_wc) cloud_filename = os.path.realpath(os.path.join(save_dir, self.sum_op.summary_name, self.sum_op.summary_name + '_word_cloud.svg')) diff --git a/hed/tools/visualization/tag_word_cloud.py b/hed/tools/visualization/tag_word_cloud.py index eaf8375d..59c586af 100644 --- a/hed/tools/visualization/tag_word_cloud.py +++ b/hed/tools/visualization/tag_word_cloud.py @@ -2,11 +2,12 @@ import numpy as np from PIL import Image +from hed.errors import HedFileError from hed.tools.visualization.word_cloud_util import default_color_func, WordCloud, generate_contour_svg import matplotlib.font_manager as fm -def create_wordcloud(word_dict, mask_path=None, background_color=None, width=400, height=300, font_path=None, **kwargs): +def create_wordcloud(word_dict, mask_path=None, background_color=None, width=400, height=300, set_font=False, **kwargs): """ Takes a word dict and returns a generated word cloud object. Parameters: @@ -45,11 +46,12 @@ def create_wordcloud(word_dict, mask_path=None, background_color=None, width=400 kwargs.setdefault('relative_scaling', 1) kwargs.setdefault('max_font_size', height / 20) kwargs.setdefault('min_font_size', 8) - if font_path and not font_path.endswith(".ttf") and not font_path.endswith(".otf")\ - and not font_path.endswith(".TTF") and not font_path.endswith(".OTF"): - font_path = fm.findfont(font_path) + if not set_font or 'font_path' not in kwargs: + kwargs['font_path'] = None + elif kwargs['font_path'] and not kwargs['font_path'].endswith((".ttf", ".otf", ".TTF", ".OTF")): + raise HedFileError("InvalidFontPath", f"Font {kwargs['font_path']} not valid on this system", "") - wc = WordCloud(font_path=font_path, background_color=background_color, mask=mask_image, + wc = WordCloud(background_color=background_color, mask=mask_image, width=width, height=height, mode="RGBA", **kwargs) wc.generate_from_frequencies(word_dict) @@ -112,4 +114,4 @@ def load_and_resize_mask(mask_path, width=None, height=None): (mask_image_array[:, :, 1] > 127) & (mask_image_array[:, :, 2] > 127)), 255, 0) - return mask_image_array.astype(np.uint8) \ No newline at end of file + return mask_image_array.astype(np.uint8) diff --git a/tests/tools/analysis/test_event_manager.py b/tests/tools/analysis/test_event_manager.py index ec364bac..aee49610 100644 --- a/tests/tools/analysis/test_event_manager.py +++ b/tests/tools/analysis/test_event_manager.py @@ -86,10 +86,6 @@ def test_onset_ordering_mixed(self): manager = EventManager(TabularInput(df), self.schema) self.assertIsInstance(manager, EventManager) hed, base, context = manager.unfold_context() - print("to here") - # df = pd.DataFrame( - # {'onset': [1, 2, '3', np.nan, 5], 'HED': ['Sensory-event', 'Red', 'Blue', 'Green', 'Label/1']}) - # pass def test_onset_ordering_bad(self): df = pd.DataFrame({'onset': [1, 2, '3', 'n/a', 5], @@ -103,6 +99,12 @@ def test_onset_ordering_bad(self): EventManager(TabularInput(df1), self.schema) self.assertEqual(ex1.args(0), "OnsetsNotOrdered") + df2 = pd.DataFrame({'onset': [1, np.nan, 1.4, 6, 5], + 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'n/a', 'Label/1']}) + with self.assertRaises(HedFileError) as ex2: + EventManager(TabularInput(df2), self.schema) + self.assertEqual(ex2.args(0), "OnsetsNotOrdered") + def test_duration_context(self): df = pd.DataFrame({'onset': [1, 2, 3, 4, 5], 'HED': ['(Duration/4.0 s, Black)', '(Duration/2 s, Red)', 'Blue', 'n/a', 'Label/1']}) @@ -111,10 +113,5 @@ def test_duration_context(self): pass - # df = pd.DataFrame( - # {'onset': [1, 2, '3', np.nan, 5], 'HED': ['Sensory-event', 'Red', 'Blue', 'Green', 'Label/1']}) - # pass - - if __name__ == '__main__': unittest.main()