From 8cfd070883391e3029e3df3e1ac52eaa5ed2233f Mon Sep 17 00:00:00 2001 From: Greta Iapalucci Date: Fri, 13 Dec 2024 11:34:37 +0100 Subject: [PATCH 1/3] A new ass_subtitle_export notebook was added containing the exportation of the race along with pre-existing gender and age estimations --- ass_subtitle_export_with_race.ipynb | 196 ++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 ass_subtitle_export_with_race.ipynb diff --git a/ass_subtitle_export_with_race.ipynb b/ass_subtitle_export_with_race.ipynb new file mode 100644 index 0000000..890e104 --- /dev/null +++ b/ass_subtitle_export_with_race.ipynb @@ -0,0 +1,196 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 78, + "id": "600f8011-eada-416e-a979-9821dcb954ea", + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python\n", + "# encoding: utf-8\n", + "\n", + "# The MIT License\n", + "\n", + "# Copyright (c) 2019-2021 Ina (David Doukhan & Zohra Rezgui - http://www.ina.fr/)\n", + "\n", + "# Permission is hereby granted, free of charge, to any person obtaining a copy\n", + "# of this software and associated documentation files (the \"Software\"), to deal\n", + "# in the Software without restriction, including without limitation the rights\n", + "# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n", + "# copies of the Software, and to permit persons to whom the Software is\n", + "# furnished to do so, subject to the following conditions:\n", + "\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n", + "# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n", + "# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n", + "# THE SOFTWARE.\n", + "\n", + "\"\"\"\n", + "Module :mod:`inaFaceAnalyzer.display_utils` contains functions allowing to\n", + "export video analysis results to formats allowing to display incrusted face\n", + "detection bounding boxes and classification estimates.\n", + "\n", + "- :func:`ass_subtitle_export` allows to export results as ASS subtitles (faster).\n", + "- :func:`video_export` generate a new video with incrusted information (longer).\n", + "\n", + "Display functions are currenly limited to the results obtained\n", + "with :class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoAnalyzer` and\n", + ":class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoTracking` analysis pipelines.\n", + "\n", + ">>> from inaFaceAnalyzer.inaFaceAnalyzer import VideoAnalyzer\n", + ">>> from inaFaceAnalyzer.display_utils import ass_subtitle_export\n", + ">>> va = VideoAnalyzer()\n", + ">>> input_vid = './media/pexels-artem-podrez-5725953.mp4'\n", + ">>> # define analysis_fps=2 in order to process 2 image frames per second of video\n", + ">>> # analysis_fps should be used for analysis AND subtitle export\n", + ">>> analysis_fps = 2\n", + ">>> df = va(input_vid, fps=analysis_fps)\n", + ">>> # export results to ass subtitle\n", + ">>> ass_subtitle_export(vid_src, df, './mysubtitle.ass', analysis_fps=analysis_fps)\n", + "\"\"\"\n", + "\n", + "#sample_vid = r'frontiere_2.mp4'\n", + "#df_fps1 = va(sample_vid, fps=1)\n", + "\n", + "import cv2\n", + "import tempfile\n", + "import os\n", + "import pandas as pd\n", + "from Cheetah.Template import Template\n", + "import datetime\n", + "from inaFaceAnalyzer.opencv_utils import video_iterator, get_video_properties, analysisFPS2subsamp_coeff\n", + "\n", + "def _hex2rgb(hx):\n", + " return (int(hx[0:2],16), int(hx[2:4],16), int(hx[4:6],16))\n", + "\n", + "\n", + "def _sec2hmsms(s):\n", + " td = datetime.timedelta(seconds=s)\n", + " h,m,s = str(td).split(':')\n", + " return '%d:%d:%.2f' % (int(h), int(m), float(s))\n", + "\n", + "def _analysis2displaydf(df, fps, subsamp_coeff, text_pat=None, cols=None):\n", + " \"\"\"\n", + " Convert analysis results to a generic pandas dataframe containing\n", + " formatted information to be displayed using export functions defined below.\n", + " \"\"\"\n", + " if isinstance(df, str):\n", + " df = pd.read_csv(df)\n", + " df.bbox = df.bbox.map(lambda x: eval(x))\n", + "\n", + " ret = pd.DataFrame()\n", + " ret['frame'] = df.frame\n", + " ret['bbox'] = df.bbox\n", + " ret[['x1', 'y1', 'x2', 'y2']] = df.apply(lambda x: x.bbox, axis=1, result_type=\"expand\")\n", + " ret['start'] = df.frame.map(lambda x: _sec2hmsms(x / fps))\n", + " ret['stop'] = df.frame.map(lambda x: _sec2hmsms((x + subsamp_coeff) / fps))\n", + "\n", + " if text_pat is None:\n", + " if 'face_id' in df.columns:\n", + " ret['rgb_color'] = df.sex_label_avg.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", + " text_pat = 'id: %s'\n", + " cols = ['face_id']\n", + " if 'sex_label_avg' in df.columns:\n", + " text_pat += ' - sex: %s (%.1f)'\n", + " cols += ['sex_label_avg', 'sex_decfunc_avg']\n", + " if 'age_label_avg' in df.columns:\n", + " text_pat += ' - age: %.1f'\n", + " cols += ['age_label_avg']\n", + " else:\n", + " text_pat = 'sex: %s (%.1f)'\n", + " cols = ['sex_label', 'sex_decfunc']\n", + " ret['rgb_color'] = df.sex_label.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", + " if 'age_label' in df.columns:\n", + " text_pat += ' - age: %.1f'\n", + " cols += ['age_label']\n", + " if 'race_label' in df.columns:\n", + " text_pat += ' - race: %s'\n", + " cols += ['race_label']\n", + "\n", + " ret['bgr_color'] = ret.rgb_color.map(lambda x: x[4:] + x[2:4] + x[:2])\n", + " ret['text'] = df.apply(lambda x: text_pat % tuple([x[e] for e in cols if e in x]), axis=1)\n", + " return ret\n", + "\n", + "def ass_subtitle_export(vid_src, result_df, ass_dst, analysis_fps=None):\n", + " \"\"\"\n", + " Export inaFaceAnalyzer results to\n", + " `ASS subtitles `_ .\n", + " ASS can embed complex shapes such as annotated face bounding boxes and\n", + " classification predictions.\n", + "\n", + " Subtitles are a good option for sharing results, since they do not require\n", + " a large amount of storage size, and do not alter original videos.\n", + " Ass subtitles can be displayed in `VLC `_,\n", + " `Aegisub `_\n", + " or `ELAN `_ annotation software.\n", + "\n", + " >>> # displaying mysample_FP2.ass subtitle with vlc\n", + " >>> vlc --sub-file ./mysample_FPS2.ass ./sample_vid.mp4\n", + "\n", + " Args:\n", + " vid_src (str): path to the input video.\n", + " result_df (str or pandas.DataFrame): video analysis result provided as :class:`pandas.DataFrame` or path to saved csv.\n", + " ass_dst (str): output filepath used to save the resulting subtitle. Must have ass extension.\n", + " analysis_fps (numeric or None, optional): Amount of frames per second which were analyzed \\\n", + " (fps analysis argument) \\\n", + " if set to None, then consider that all video frames were processed. Defaults to None.\n", + " \"\"\"\n", + "\n", + "\n", + " assert ass_dst[-4:].lower() == '.ass', ass_dst\n", + "\n", + " video_props = get_video_properties(vid_src)\n", + " fps, width, height = [video_props[e] for e in ['fps', 'width', 'height']]\n", + "\n", + " if analysis_fps is None:\n", + " subsamp_coeff = 1\n", + " else:\n", + " subsamp_coeff = analysisFPS2subsamp_coeff(vid_src, analysis_fps)\n", + "\n", + " displaydf = _analysis2displaydf(result_df, fps, subsamp_coeff)\n", + "\n", + " p = os.getcwd()\n", + " t = Template(file = p + '/template.ass')\n", + "\n", + " t.height = height\n", + " t.width = width\n", + " t.display_df = displaydf\n", + " # text font size set to 4% video height\n", + " t.text_font_size = int(0.04 * height)\n", + "\n", + " with open(ass_dst, 'wt') as fid:\n", + " print(t, file=fid)\n", + "\n", + "#ass_subtitle_export(sample_vid, df_fps1, './my_sample_fps1.ass', analysis_fps=1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From bf1860a2106cae8fe805c52231a36ad346844409 Mon Sep 17 00:00:00 2001 From: Greta Iapalucci Date: Fri, 13 Dec 2024 13:15:34 +0100 Subject: [PATCH 2/3] A new ass_subtitle_export notebook was added containing the exportation of the race along with pre-existing gender and age estimations --- .../ass_subtitle_export_with_race.ipynb | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 inaFaceAnalyzer/ass_subtitle_export_with_race.ipynb diff --git a/inaFaceAnalyzer/ass_subtitle_export_with_race.ipynb b/inaFaceAnalyzer/ass_subtitle_export_with_race.ipynb new file mode 100644 index 0000000..890e104 --- /dev/null +++ b/inaFaceAnalyzer/ass_subtitle_export_with_race.ipynb @@ -0,0 +1,196 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 78, + "id": "600f8011-eada-416e-a979-9821dcb954ea", + "metadata": {}, + "outputs": [], + "source": [ + "#!/usr/bin/env python\n", + "# encoding: utf-8\n", + "\n", + "# The MIT License\n", + "\n", + "# Copyright (c) 2019-2021 Ina (David Doukhan & Zohra Rezgui - http://www.ina.fr/)\n", + "\n", + "# Permission is hereby granted, free of charge, to any person obtaining a copy\n", + "# of this software and associated documentation files (the \"Software\"), to deal\n", + "# in the Software without restriction, including without limitation the rights\n", + "# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n", + "# copies of the Software, and to permit persons to whom the Software is\n", + "# furnished to do so, subject to the following conditions:\n", + "\n", + "# The above copyright notice and this permission notice shall be included in\n", + "# all copies or substantial portions of the Software.\n", + "\n", + "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", + "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", + "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n", + "# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", + "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n", + "# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n", + "# THE SOFTWARE.\n", + "\n", + "\"\"\"\n", + "Module :mod:`inaFaceAnalyzer.display_utils` contains functions allowing to\n", + "export video analysis results to formats allowing to display incrusted face\n", + "detection bounding boxes and classification estimates.\n", + "\n", + "- :func:`ass_subtitle_export` allows to export results as ASS subtitles (faster).\n", + "- :func:`video_export` generate a new video with incrusted information (longer).\n", + "\n", + "Display functions are currenly limited to the results obtained\n", + "with :class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoAnalyzer` and\n", + ":class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoTracking` analysis pipelines.\n", + "\n", + ">>> from inaFaceAnalyzer.inaFaceAnalyzer import VideoAnalyzer\n", + ">>> from inaFaceAnalyzer.display_utils import ass_subtitle_export\n", + ">>> va = VideoAnalyzer()\n", + ">>> input_vid = './media/pexels-artem-podrez-5725953.mp4'\n", + ">>> # define analysis_fps=2 in order to process 2 image frames per second of video\n", + ">>> # analysis_fps should be used for analysis AND subtitle export\n", + ">>> analysis_fps = 2\n", + ">>> df = va(input_vid, fps=analysis_fps)\n", + ">>> # export results to ass subtitle\n", + ">>> ass_subtitle_export(vid_src, df, './mysubtitle.ass', analysis_fps=analysis_fps)\n", + "\"\"\"\n", + "\n", + "#sample_vid = r'frontiere_2.mp4'\n", + "#df_fps1 = va(sample_vid, fps=1)\n", + "\n", + "import cv2\n", + "import tempfile\n", + "import os\n", + "import pandas as pd\n", + "from Cheetah.Template import Template\n", + "import datetime\n", + "from inaFaceAnalyzer.opencv_utils import video_iterator, get_video_properties, analysisFPS2subsamp_coeff\n", + "\n", + "def _hex2rgb(hx):\n", + " return (int(hx[0:2],16), int(hx[2:4],16), int(hx[4:6],16))\n", + "\n", + "\n", + "def _sec2hmsms(s):\n", + " td = datetime.timedelta(seconds=s)\n", + " h,m,s = str(td).split(':')\n", + " return '%d:%d:%.2f' % (int(h), int(m), float(s))\n", + "\n", + "def _analysis2displaydf(df, fps, subsamp_coeff, text_pat=None, cols=None):\n", + " \"\"\"\n", + " Convert analysis results to a generic pandas dataframe containing\n", + " formatted information to be displayed using export functions defined below.\n", + " \"\"\"\n", + " if isinstance(df, str):\n", + " df = pd.read_csv(df)\n", + " df.bbox = df.bbox.map(lambda x: eval(x))\n", + "\n", + " ret = pd.DataFrame()\n", + " ret['frame'] = df.frame\n", + " ret['bbox'] = df.bbox\n", + " ret[['x1', 'y1', 'x2', 'y2']] = df.apply(lambda x: x.bbox, axis=1, result_type=\"expand\")\n", + " ret['start'] = df.frame.map(lambda x: _sec2hmsms(x / fps))\n", + " ret['stop'] = df.frame.map(lambda x: _sec2hmsms((x + subsamp_coeff) / fps))\n", + "\n", + " if text_pat is None:\n", + " if 'face_id' in df.columns:\n", + " ret['rgb_color'] = df.sex_label_avg.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", + " text_pat = 'id: %s'\n", + " cols = ['face_id']\n", + " if 'sex_label_avg' in df.columns:\n", + " text_pat += ' - sex: %s (%.1f)'\n", + " cols += ['sex_label_avg', 'sex_decfunc_avg']\n", + " if 'age_label_avg' in df.columns:\n", + " text_pat += ' - age: %.1f'\n", + " cols += ['age_label_avg']\n", + " else:\n", + " text_pat = 'sex: %s (%.1f)'\n", + " cols = ['sex_label', 'sex_decfunc']\n", + " ret['rgb_color'] = df.sex_label.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", + " if 'age_label' in df.columns:\n", + " text_pat += ' - age: %.1f'\n", + " cols += ['age_label']\n", + " if 'race_label' in df.columns:\n", + " text_pat += ' - race: %s'\n", + " cols += ['race_label']\n", + "\n", + " ret['bgr_color'] = ret.rgb_color.map(lambda x: x[4:] + x[2:4] + x[:2])\n", + " ret['text'] = df.apply(lambda x: text_pat % tuple([x[e] for e in cols if e in x]), axis=1)\n", + " return ret\n", + "\n", + "def ass_subtitle_export(vid_src, result_df, ass_dst, analysis_fps=None):\n", + " \"\"\"\n", + " Export inaFaceAnalyzer results to\n", + " `ASS subtitles `_ .\n", + " ASS can embed complex shapes such as annotated face bounding boxes and\n", + " classification predictions.\n", + "\n", + " Subtitles are a good option for sharing results, since they do not require\n", + " a large amount of storage size, and do not alter original videos.\n", + " Ass subtitles can be displayed in `VLC `_,\n", + " `Aegisub `_\n", + " or `ELAN `_ annotation software.\n", + "\n", + " >>> # displaying mysample_FP2.ass subtitle with vlc\n", + " >>> vlc --sub-file ./mysample_FPS2.ass ./sample_vid.mp4\n", + "\n", + " Args:\n", + " vid_src (str): path to the input video.\n", + " result_df (str or pandas.DataFrame): video analysis result provided as :class:`pandas.DataFrame` or path to saved csv.\n", + " ass_dst (str): output filepath used to save the resulting subtitle. Must have ass extension.\n", + " analysis_fps (numeric or None, optional): Amount of frames per second which were analyzed \\\n", + " (fps analysis argument) \\\n", + " if set to None, then consider that all video frames were processed. Defaults to None.\n", + " \"\"\"\n", + "\n", + "\n", + " assert ass_dst[-4:].lower() == '.ass', ass_dst\n", + "\n", + " video_props = get_video_properties(vid_src)\n", + " fps, width, height = [video_props[e] for e in ['fps', 'width', 'height']]\n", + "\n", + " if analysis_fps is None:\n", + " subsamp_coeff = 1\n", + " else:\n", + " subsamp_coeff = analysisFPS2subsamp_coeff(vid_src, analysis_fps)\n", + "\n", + " displaydf = _analysis2displaydf(result_df, fps, subsamp_coeff)\n", + "\n", + " p = os.getcwd()\n", + " t = Template(file = p + '/template.ass')\n", + "\n", + " t.height = height\n", + " t.width = width\n", + " t.display_df = displaydf\n", + " # text font size set to 4% video height\n", + " t.text_font_size = int(0.04 * height)\n", + "\n", + " with open(ass_dst, 'wt') as fid:\n", + " print(t, file=fid)\n", + "\n", + "#ass_subtitle_export(sample_vid, df_fps1, './my_sample_fps1.ass', analysis_fps=1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 34ff5a595ee60e531956fa3298fa76b0e38caa40 Mon Sep 17 00:00:00 2001 From: Greta Iapalucci <136159188+gretaia@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:25:04 +0100 Subject: [PATCH 3/3] Delete ass_subtitle_export_with_race.ipynb --- ass_subtitle_export_with_race.ipynb | 196 ---------------------------- 1 file changed, 196 deletions(-) delete mode 100644 ass_subtitle_export_with_race.ipynb diff --git a/ass_subtitle_export_with_race.ipynb b/ass_subtitle_export_with_race.ipynb deleted file mode 100644 index 890e104..0000000 --- a/ass_subtitle_export_with_race.ipynb +++ /dev/null @@ -1,196 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 78, - "id": "600f8011-eada-416e-a979-9821dcb954ea", - "metadata": {}, - "outputs": [], - "source": [ - "#!/usr/bin/env python\n", - "# encoding: utf-8\n", - "\n", - "# The MIT License\n", - "\n", - "# Copyright (c) 2019-2021 Ina (David Doukhan & Zohra Rezgui - http://www.ina.fr/)\n", - "\n", - "# Permission is hereby granted, free of charge, to any person obtaining a copy\n", - "# of this software and associated documentation files (the \"Software\"), to deal\n", - "# in the Software without restriction, including without limitation the rights\n", - "# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n", - "# copies of the Software, and to permit persons to whom the Software is\n", - "# furnished to do so, subject to the following conditions:\n", - "\n", - "# The above copyright notice and this permission notice shall be included in\n", - "# all copies or substantial portions of the Software.\n", - "\n", - "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n", - "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n", - "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n", - "# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n", - "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n", - "# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n", - "# THE SOFTWARE.\n", - "\n", - "\"\"\"\n", - "Module :mod:`inaFaceAnalyzer.display_utils` contains functions allowing to\n", - "export video analysis results to formats allowing to display incrusted face\n", - "detection bounding boxes and classification estimates.\n", - "\n", - "- :func:`ass_subtitle_export` allows to export results as ASS subtitles (faster).\n", - "- :func:`video_export` generate a new video with incrusted information (longer).\n", - "\n", - "Display functions are currenly limited to the results obtained\n", - "with :class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoAnalyzer` and\n", - ":class:`inaFaceAnalyzer.inaFaceAnalyzer.VideoTracking` analysis pipelines.\n", - "\n", - ">>> from inaFaceAnalyzer.inaFaceAnalyzer import VideoAnalyzer\n", - ">>> from inaFaceAnalyzer.display_utils import ass_subtitle_export\n", - ">>> va = VideoAnalyzer()\n", - ">>> input_vid = './media/pexels-artem-podrez-5725953.mp4'\n", - ">>> # define analysis_fps=2 in order to process 2 image frames per second of video\n", - ">>> # analysis_fps should be used for analysis AND subtitle export\n", - ">>> analysis_fps = 2\n", - ">>> df = va(input_vid, fps=analysis_fps)\n", - ">>> # export results to ass subtitle\n", - ">>> ass_subtitle_export(vid_src, df, './mysubtitle.ass', analysis_fps=analysis_fps)\n", - "\"\"\"\n", - "\n", - "#sample_vid = r'frontiere_2.mp4'\n", - "#df_fps1 = va(sample_vid, fps=1)\n", - "\n", - "import cv2\n", - "import tempfile\n", - "import os\n", - "import pandas as pd\n", - "from Cheetah.Template import Template\n", - "import datetime\n", - "from inaFaceAnalyzer.opencv_utils import video_iterator, get_video_properties, analysisFPS2subsamp_coeff\n", - "\n", - "def _hex2rgb(hx):\n", - " return (int(hx[0:2],16), int(hx[2:4],16), int(hx[4:6],16))\n", - "\n", - "\n", - "def _sec2hmsms(s):\n", - " td = datetime.timedelta(seconds=s)\n", - " h,m,s = str(td).split(':')\n", - " return '%d:%d:%.2f' % (int(h), int(m), float(s))\n", - "\n", - "def _analysis2displaydf(df, fps, subsamp_coeff, text_pat=None, cols=None):\n", - " \"\"\"\n", - " Convert analysis results to a generic pandas dataframe containing\n", - " formatted information to be displayed using export functions defined below.\n", - " \"\"\"\n", - " if isinstance(df, str):\n", - " df = pd.read_csv(df)\n", - " df.bbox = df.bbox.map(lambda x: eval(x))\n", - "\n", - " ret = pd.DataFrame()\n", - " ret['frame'] = df.frame\n", - " ret['bbox'] = df.bbox\n", - " ret[['x1', 'y1', 'x2', 'y2']] = df.apply(lambda x: x.bbox, axis=1, result_type=\"expand\")\n", - " ret['start'] = df.frame.map(lambda x: _sec2hmsms(x / fps))\n", - " ret['stop'] = df.frame.map(lambda x: _sec2hmsms((x + subsamp_coeff) / fps))\n", - "\n", - " if text_pat is None:\n", - " if 'face_id' in df.columns:\n", - " ret['rgb_color'] = df.sex_label_avg.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", - " text_pat = 'id: %s'\n", - " cols = ['face_id']\n", - " if 'sex_label_avg' in df.columns:\n", - " text_pat += ' - sex: %s (%.1f)'\n", - " cols += ['sex_label_avg', 'sex_decfunc_avg']\n", - " if 'age_label_avg' in df.columns:\n", - " text_pat += ' - age: %.1f'\n", - " cols += ['age_label_avg']\n", - " else:\n", - " text_pat = 'sex: %s (%.1f)'\n", - " cols = ['sex_label', 'sex_decfunc']\n", - " ret['rgb_color'] = df.sex_label.map(lambda x: '0000FF' if x == 'm' else '00FF00')\n", - " if 'age_label' in df.columns:\n", - " text_pat += ' - age: %.1f'\n", - " cols += ['age_label']\n", - " if 'race_label' in df.columns:\n", - " text_pat += ' - race: %s'\n", - " cols += ['race_label']\n", - "\n", - " ret['bgr_color'] = ret.rgb_color.map(lambda x: x[4:] + x[2:4] + x[:2])\n", - " ret['text'] = df.apply(lambda x: text_pat % tuple([x[e] for e in cols if e in x]), axis=1)\n", - " return ret\n", - "\n", - "def ass_subtitle_export(vid_src, result_df, ass_dst, analysis_fps=None):\n", - " \"\"\"\n", - " Export inaFaceAnalyzer results to\n", - " `ASS subtitles `_ .\n", - " ASS can embed complex shapes such as annotated face bounding boxes and\n", - " classification predictions.\n", - "\n", - " Subtitles are a good option for sharing results, since they do not require\n", - " a large amount of storage size, and do not alter original videos.\n", - " Ass subtitles can be displayed in `VLC `_,\n", - " `Aegisub `_\n", - " or `ELAN `_ annotation software.\n", - "\n", - " >>> # displaying mysample_FP2.ass subtitle with vlc\n", - " >>> vlc --sub-file ./mysample_FPS2.ass ./sample_vid.mp4\n", - "\n", - " Args:\n", - " vid_src (str): path to the input video.\n", - " result_df (str or pandas.DataFrame): video analysis result provided as :class:`pandas.DataFrame` or path to saved csv.\n", - " ass_dst (str): output filepath used to save the resulting subtitle. Must have ass extension.\n", - " analysis_fps (numeric or None, optional): Amount of frames per second which were analyzed \\\n", - " (fps analysis argument) \\\n", - " if set to None, then consider that all video frames were processed. Defaults to None.\n", - " \"\"\"\n", - "\n", - "\n", - " assert ass_dst[-4:].lower() == '.ass', ass_dst\n", - "\n", - " video_props = get_video_properties(vid_src)\n", - " fps, width, height = [video_props[e] for e in ['fps', 'width', 'height']]\n", - "\n", - " if analysis_fps is None:\n", - " subsamp_coeff = 1\n", - " else:\n", - " subsamp_coeff = analysisFPS2subsamp_coeff(vid_src, analysis_fps)\n", - "\n", - " displaydf = _analysis2displaydf(result_df, fps, subsamp_coeff)\n", - "\n", - " p = os.getcwd()\n", - " t = Template(file = p + '/template.ass')\n", - "\n", - " t.height = height\n", - " t.width = width\n", - " t.display_df = displaydf\n", - " # text font size set to 4% video height\n", - " t.text_font_size = int(0.04 * height)\n", - "\n", - " with open(ass_dst, 'wt') as fid:\n", - " print(t, file=fid)\n", - "\n", - "#ass_subtitle_export(sample_vid, df_fps1, './my_sample_fps1.ass', analysis_fps=1)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}