diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c578111..5c02044 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: - name: Install linting tools run: | python -m pip install --upgrade pip - pip install ruff black isort + pip install ruff black isort mypy - name: Check code formatting with Black run: black --check src/aylm tests @@ -69,3 +69,6 @@ jobs: - name: Lint with Ruff run: ruff check src/aylm tests + + - name: Type check with mypy (informational) + run: mypy src/aylm --ignore-missing-imports || echo "mypy found issues (non-blocking)" diff --git a/.output.txt b/.output.txt new file mode 100644 index 0000000..903b649 --- /dev/null +++ b/.output.txt @@ -0,0 +1,4488 @@ +async-dropdown.js.map +async-graph.js +async-graph.js.map +async-highlight.js +async-highlight.js.map +async-markdown.js +async-markdown.js.LICENSE.txt +async-markdown.js.map +async-mathjax.js +async-slider.js +async-slider.js.map +async-upload.js +async-upload.js.LICENSE.txt +async-upload.js.map +dash_core_components-shared.js +dash_core_components-shared.js.LICENSE.txt +dash_core_components-shared.js.map +dash_core_components.js +dash_core_components.js.LICENSE.txt +dash_core_components.js.map +express.py +metadata.json +package-info.json +proptypes.js +./aylm_env/lib/python3.11/site-packages/dash/dcc/__pycache__: +Button.cpython-311-pytest-9.0.2.pyc +Button.cpython-311.pyc +Checklist.cpython-311-pytest-9.0.2.pyc +Checklist.cpython-311.pyc +Clipboard.cpython-311-pytest-9.0.2.pyc +Clipboard.cpython-311.pyc +ConfirmDialog.cpython-311-pytest-9.0.2.pyc +ConfirmDialog.cpython-311.pyc +ConfirmDialogProvider.cpython-311-pytest-9.0.2.pyc +ConfirmDialogProvider.cpython-311.pyc +DatePickerRange.cpython-311-pytest-9.0.2.pyc +DatePickerRange.cpython-311.pyc +DatePickerSingle.cpython-311-pytest-9.0.2.pyc +DatePickerSingle.cpython-311.pyc +Download.cpython-311-pytest-9.0.2.pyc +Download.cpython-311.pyc +Dropdown.cpython-311-pytest-9.0.2.pyc +Dropdown.cpython-311.pyc +Geolocation.cpython-311-pytest-9.0.2.pyc +Geolocation.cpython-311.pyc +Graph.cpython-311-pytest-9.0.2.pyc +Graph.cpython-311.pyc +Input.cpython-311-pytest-9.0.2.pyc +Input.cpython-311.pyc +Interval.cpython-311-pytest-9.0.2.pyc +Interval.cpython-311.pyc +Link.cpython-311-pytest-9.0.2.pyc +Link.cpython-311.pyc +Loading.cpython-311-pytest-9.0.2.pyc +Loading.cpython-311.pyc +Location.cpython-311-pytest-9.0.2.pyc +Location.cpython-311.pyc +Markdown.cpython-311-pytest-9.0.2.pyc +Markdown.cpython-311.pyc +RadioItems.cpython-311-pytest-9.0.2.pyc +RadioItems.cpython-311.pyc +RangeSlider.cpython-311-pytest-9.0.2.pyc +RangeSlider.cpython-311.pyc +Slider.cpython-311-pytest-9.0.2.pyc +Slider.cpython-311.pyc +Store.cpython-311-pytest-9.0.2.pyc +Store.cpython-311.pyc +Tab.cpython-311-pytest-9.0.2.pyc +Tab.cpython-311.pyc +Tabs.cpython-311-pytest-9.0.2.pyc +Tabs.cpython-311.pyc +Textarea.cpython-311-pytest-9.0.2.pyc +Textarea.cpython-311.pyc +Tooltip.cpython-311-pytest-9.0.2.pyc +Tooltip.cpython-311.pyc +Upload.cpython-311-pytest-9.0.2.pyc +Upload.cpython-311.pyc +__init__.cpython-311-pytest-9.0.2.pyc +__init__.cpython-311.pyc +_imports_.cpython-311-pytest-9.0.2.pyc +_imports_.cpython-311.pyc +express.cpython-311-pytest-9.0.2.pyc +express.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dash/deps: +polyfill@7.12.1.min.js react-dom@18.3.1.min.js +prop-types@15.8.1.js react@16.14.0.js +prop-types@15.8.1.min.js react@16.14.0.min.js +react-dom@16.14.0.js react@18.2.0.js +react-dom@16.14.0.min.js react@18.2.0.min.js +react-dom@18.2.0.js react@18.3.1.js +react-dom@18.2.0.min.js react@18.3.1.min.js +react-dom@18.3.1.js +./aylm_env/lib/python3.11/site-packages/dash/development: +__init__.py _py_prop_typing.py +__pycache__ _r_components_generation.py +_all_keywords.py base_component.py +_collect_nodes.py build_process.py +_generate_prop_types.py component_generator.py +_jl_components_generation.py update_components.py +_py_components_generation.py +./aylm_env/lib/python3.11/site-packages/dash/development/__pycache__: +__init__.cpython-311-pytest-9.0.2.pyc +__init__.cpython-311.pyc +_all_keywords.cpython-311.pyc +_collect_nodes.cpython-311.pyc +_generate_prop_types.cpython-311.pyc +_jl_components_generation.cpython-311.pyc +_py_components_generation.cpython-311.pyc +_py_prop_typing.cpython-311.pyc +_r_components_generation.cpython-311.pyc +base_component.cpython-311-pytest-9.0.2.pyc +base_component.cpython-311.pyc +build_process.cpython-311.pyc +component_generator.cpython-311.pyc +update_components.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dash/html: +A.py MapEl.py +Abbr.py Mark.py +Acronym.py Marquee.py +Address.py Meta.py +Area.py Meter.py +Article.py Nav.py +Aside.py Nobr.py +Audio.py Noscript.py +B.py ObjectEl.py +Base.py Ol.py +Basefont.py Optgroup.py +Bdi.py Option.py +Bdo.py Output.py +Big.py P.py +Blink.py Param.py +Blockquote.py Picture.py +Br.py Plaintext.py +Button.py Pre.py +Canvas.py Progress.py +Caption.py Q.py +Center.py Rb.py +Cite.py Rp.py +Code.py Rt.py +Col.py Rtc.py +Colgroup.py Ruby.py +Content.py S.py +Data.py Samp.py +Datalist.py Script.py +Dd.py Section.py +Del.py Select.py +Details.py Shadow.py +Dfn.py Slot.py +Dialog.py Small.py +Div.py Source.py +Dl.py Spacer.py +Dt.py Span.py +Em.py Strike.py +Embed.py Strong.py +Fieldset.py Sub.py +Figcaption.py Summary.py +Figure.py Sup.py +Font.py Table.py +Footer.py Tbody.py +Form.py Td.py +Frame.py Template.py +Frameset.py Textarea.py +H1.py Tfoot.py +H2.py Th.py +H3.py Thead.py +H4.py Time.py +H5.py Title.py +H6.py Tr.py +Header.py Track.py +Hgroup.py U.py +Hr.py Ul.py +I.py Var.py +Iframe.py Video.py +Img.py Wbr.py +Ins.py Xmp.py +Kbd.py __init__.py +Keygen.py __pycache__ +Label.py _imports_.py +Legend.py dash_html_components.min.js +Li.py dash_html_components.min.js.map +Link.py metadata.json +Main.py package-info.json +./aylm_env/lib/python3.11/site-packages/dash/html/__pycache__: +A.cpython-311-pytest-9.0.2.pyc Li.cpython-311.pyc +A.cpython-311.pyc Link.cpython-311-pytest-9.0.2.pyc +Abbr.cpython-311-pytest-9.0.2.pyc Link.cpython-311.pyc +Abbr.cpython-311.pyc Main.cpython-311-pytest-9.0.2.pyc +Acronym.cpython-311-pytest-9.0.2.pyc Main.cpython-311.pyc +Acronym.cpython-311.pyc MapEl.cpython-311-pytest-9.0.2.pyc +Address.cpython-311-pytest-9.0.2.pyc MapEl.cpython-311.pyc +Address.cpython-311.pyc Mark.cpython-311-pytest-9.0.2.pyc +Area.cpython-311-pytest-9.0.2.pyc Mark.cpython-311.pyc +Area.cpython-311.pyc Marquee.cpython-311-pytest-9.0.2.pyc +Article.cpython-311-pytest-9.0.2.pyc Marquee.cpython-311.pyc +Article.cpython-311.pyc Meta.cpython-311-pytest-9.0.2.pyc +Aside.cpython-311-pytest-9.0.2.pyc Meta.cpython-311.pyc +Aside.cpython-311.pyc Meter.cpython-311-pytest-9.0.2.pyc +Audio.cpython-311-pytest-9.0.2.pyc Meter.cpython-311.pyc +Audio.cpython-311.pyc Nav.cpython-311-pytest-9.0.2.pyc +B.cpython-311-pytest-9.0.2.pyc Nav.cpython-311.pyc +B.cpython-311.pyc Nobr.cpython-311-pytest-9.0.2.pyc +Base.cpython-311-pytest-9.0.2.pyc Nobr.cpython-311.pyc +Base.cpython-311.pyc Noscript.cpython-311-pytest-9.0.2.pyc +Basefont.cpython-311-pytest-9.0.2.pyc Noscript.cpython-311.pyc +Basefont.cpython-311.pyc ObjectEl.cpython-311-pytest-9.0.2.pyc +Bdi.cpython-311-pytest-9.0.2.pyc ObjectEl.cpython-311.pyc +Bdi.cpython-311.pyc Ol.cpython-311-pytest-9.0.2.pyc +Bdo.cpython-311-pytest-9.0.2.pyc Ol.cpython-311.pyc +Bdo.cpython-311.pyc Optgroup.cpython-311-pytest-9.0.2.pyc +Big.cpython-311-pytest-9.0.2.pyc Optgroup.cpython-311.pyc +Big.cpython-311.pyc Option.cpython-311-pytest-9.0.2.pyc +Blink.cpython-311-pytest-9.0.2.pyc Option.cpython-311.pyc +Blink.cpython-311.pyc Output.cpython-311-pytest-9.0.2.pyc +Blockquote.cpython-311-pytest-9.0.2.pyc Output.cpython-311.pyc +Blockquote.cpython-311.pyc P.cpython-311-pytest-9.0.2.pyc +Br.cpython-311-pytest-9.0.2.pyc P.cpython-311.pyc +Br.cpython-311.pyc Param.cpython-311-pytest-9.0.2.pyc +Button.cpython-311-pytest-9.0.2.pyc Param.cpython-311.pyc +Button.cpython-311.pyc Picture.cpython-311-pytest-9.0.2.pyc +Canvas.cpython-311-pytest-9.0.2.pyc Picture.cpython-311.pyc +Canvas.cpython-311.pyc Plaintext.cpython-311-pytest-9.0.2.pyc +Caption.cpython-311-pytest-9.0.2.pyc Plaintext.cpython-311.pyc +Caption.cpython-311.pyc Pre.cpython-311-pytest-9.0.2.pyc +Center.cpython-311-pytest-9.0.2.pyc Pre.cpython-311.pyc +Center.cpython-311.pyc Progress.cpython-311-pytest-9.0.2.pyc +Cite.cpython-311-pytest-9.0.2.pyc Progress.cpython-311.pyc +Cite.cpython-311.pyc Q.cpython-311-pytest-9.0.2.pyc +Code.cpython-311-pytest-9.0.2.pyc Q.cpython-311.pyc +Code.cpython-311.pyc Rb.cpython-311-pytest-9.0.2.pyc +Col.cpython-311-pytest-9.0.2.pyc Rb.cpython-311.pyc +Col.cpython-311.pyc Rp.cpython-311-pytest-9.0.2.pyc +Colgroup.cpython-311-pytest-9.0.2.pyc Rp.cpython-311.pyc +Colgroup.cpython-311.pyc Rt.cpython-311-pytest-9.0.2.pyc +Content.cpython-311-pytest-9.0.2.pyc Rt.cpython-311.pyc +Content.cpython-311.pyc Rtc.cpython-311-pytest-9.0.2.pyc +Data.cpython-311-pytest-9.0.2.pyc Rtc.cpython-311.pyc +Data.cpython-311.pyc Ruby.cpython-311-pytest-9.0.2.pyc +Datalist.cpython-311-pytest-9.0.2.pyc Ruby.cpython-311.pyc +Datalist.cpython-311.pyc S.cpython-311-pytest-9.0.2.pyc +Dd.cpython-311-pytest-9.0.2.pyc S.cpython-311.pyc +Dd.cpython-311.pyc Samp.cpython-311-pytest-9.0.2.pyc +Del.cpython-311-pytest-9.0.2.pyc Samp.cpython-311.pyc +Del.cpython-311.pyc Script.cpython-311-pytest-9.0.2.pyc +Details.cpython-311-pytest-9.0.2.pyc Script.cpython-311.pyc +Details.cpython-311.pyc Section.cpython-311-pytest-9.0.2.pyc +Dfn.cpython-311-pytest-9.0.2.pyc Section.cpython-311.pyc +Dfn.cpython-311.pyc Select.cpython-311-pytest-9.0.2.pyc +Dialog.cpython-311-pytest-9.0.2.pyc Select.cpython-311.pyc +Dialog.cpython-311.pyc Shadow.cpython-311-pytest-9.0.2.pyc +Div.cpython-311-pytest-9.0.2.pyc Shadow.cpython-311.pyc +Div.cpython-311.pyc Slot.cpython-311-pytest-9.0.2.pyc +Dl.cpython-311-pytest-9.0.2.pyc Slot.cpython-311.pyc +Dl.cpython-311.pyc Small.cpython-311-pytest-9.0.2.pyc +Dt.cpython-311-pytest-9.0.2.pyc Small.cpython-311.pyc +Dt.cpython-311.pyc Source.cpython-311-pytest-9.0.2.pyc +Em.cpython-311-pytest-9.0.2.pyc Source.cpython-311.pyc +Em.cpython-311.pyc Spacer.cpython-311-pytest-9.0.2.pyc +Embed.cpython-311-pytest-9.0.2.pyc Spacer.cpython-311.pyc +Embed.cpython-311.pyc Span.cpython-311-pytest-9.0.2.pyc +Fieldset.cpython-311-pytest-9.0.2.pyc Span.cpython-311.pyc +Fieldset.cpython-311.pyc Strike.cpython-311-pytest-9.0.2.pyc +Figcaption.cpython-311-pytest-9.0.2.pyc Strike.cpython-311.pyc +Figcaption.cpython-311.pyc Strong.cpython-311-pytest-9.0.2.pyc +Figure.cpython-311-pytest-9.0.2.pyc Strong.cpython-311.pyc +Figure.cpython-311.pyc Sub.cpython-311-pytest-9.0.2.pyc +Font.cpython-311-pytest-9.0.2.pyc Sub.cpython-311.pyc +Font.cpython-311.pyc Summary.cpython-311-pytest-9.0.2.pyc +Footer.cpython-311-pytest-9.0.2.pyc Summary.cpython-311.pyc +Footer.cpython-311.pyc Sup.cpython-311-pytest-9.0.2.pyc +Form.cpython-311-pytest-9.0.2.pyc Sup.cpython-311.pyc +Form.cpython-311.pyc Table.cpython-311-pytest-9.0.2.pyc +Frame.cpython-311-pytest-9.0.2.pyc Table.cpython-311.pyc +Frame.cpython-311.pyc Tbody.cpython-311-pytest-9.0.2.pyc +Frameset.cpython-311-pytest-9.0.2.pyc Tbody.cpython-311.pyc +Frameset.cpython-311.pyc Td.cpython-311-pytest-9.0.2.pyc +H1.cpython-311-pytest-9.0.2.pyc Td.cpython-311.pyc +H1.cpython-311.pyc Template.cpython-311-pytest-9.0.2.pyc +H2.cpython-311-pytest-9.0.2.pyc Template.cpython-311.pyc +H2.cpython-311.pyc Textarea.cpython-311-pytest-9.0.2.pyc +H3.cpython-311-pytest-9.0.2.pyc Textarea.cpython-311.pyc +H3.cpython-311.pyc Tfoot.cpython-311-pytest-9.0.2.pyc +H4.cpython-311-pytest-9.0.2.pyc Tfoot.cpython-311.pyc +H4.cpython-311.pyc Th.cpython-311-pytest-9.0.2.pyc +H5.cpython-311-pytest-9.0.2.pyc Th.cpython-311.pyc +H5.cpython-311.pyc Thead.cpython-311-pytest-9.0.2.pyc +H6.cpython-311-pytest-9.0.2.pyc Thead.cpython-311.pyc +H6.cpython-311.pyc Time.cpython-311-pytest-9.0.2.pyc +Header.cpython-311-pytest-9.0.2.pyc Time.cpython-311.pyc +Header.cpython-311.pyc Title.cpython-311-pytest-9.0.2.pyc +Hgroup.cpython-311-pytest-9.0.2.pyc Title.cpython-311.pyc +Hgroup.cpython-311.pyc Tr.cpython-311-pytest-9.0.2.pyc +Hr.cpython-311-pytest-9.0.2.pyc Tr.cpython-311.pyc +Hr.cpython-311.pyc Track.cpython-311-pytest-9.0.2.pyc +I.cpython-311-pytest-9.0.2.pyc Track.cpython-311.pyc +I.cpython-311.pyc U.cpython-311-pytest-9.0.2.pyc +Iframe.cpython-311-pytest-9.0.2.pyc U.cpython-311.pyc +Iframe.cpython-311.pyc Ul.cpython-311-pytest-9.0.2.pyc +Img.cpython-311-pytest-9.0.2.pyc Ul.cpython-311.pyc +Img.cpython-311.pyc Var.cpython-311-pytest-9.0.2.pyc +Ins.cpython-311-pytest-9.0.2.pyc Var.cpython-311.pyc +Ins.cpython-311.pyc Video.cpython-311-pytest-9.0.2.pyc +Kbd.cpython-311-pytest-9.0.2.pyc Video.cpython-311.pyc +Kbd.cpython-311.pyc Wbr.cpython-311-pytest-9.0.2.pyc +Keygen.cpython-311-pytest-9.0.2.pyc Wbr.cpython-311.pyc +Keygen.cpython-311.pyc Xmp.cpython-311-pytest-9.0.2.pyc +Label.cpython-311-pytest-9.0.2.pyc Xmp.cpython-311.pyc +Label.cpython-311.pyc __init__.cpython-311-pytest-9.0.2.pyc +Legend.cpython-311-pytest-9.0.2.pyc __init__.cpython-311.pyc +Legend.cpython-311.pyc _imports_.cpython-311-pytest-9.0.2.pyc +Li.cpython-311-pytest-9.0.2.pyc _imports_.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dash/labextension: +dist package.json +./aylm_env/lib/python3.11/site-packages/dash/labextension/dist: +dash-jupyterlab.tgz +./aylm_env/lib/python3.11/site-packages/dash/nbextension: +__init__.py __pycache__ dash.json main.js +./aylm_env/lib/python3.11/site-packages/dash/nbextension/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dash/testing: +__init__.py composite.py newhooks.py +__pycache__ consts.py plugin.py +application_runners.py dash_page.py wait.py +browser.py errors.py +./aylm_env/lib/python3.11/site-packages/dash/testing/__pycache__: +__init__.cpython-311-pytest-9.0.2.pyc +__init__.cpython-311.pyc +application_runners.cpython-311-pytest-9.0.2.pyc +application_runners.cpython-311.pyc +browser.cpython-311.pyc +composite.cpython-311.pyc +consts.cpython-311-pytest-9.0.2.pyc +consts.cpython-311.pyc +dash_page.cpython-311.pyc +errors.cpython-311.pyc +newhooks.cpython-311.pyc +plugin.cpython-311-pytest-9.0.2.pyc +plugin.cpython-311.pyc +wait.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dash-4.0.0.dist-info: +INSTALLER WHEEL top_level.txt +METADATA entry_points.txt +RECORD licenses +./aylm_env/lib/python3.11/site-packages/dash-4.0.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/dateutil: +__init__.py easter.py tz +__pycache__ parser tzwin.py +_common.py relativedelta.py utils.py +_version.py rrule.py zoneinfo +./aylm_env/lib/python3.11/site-packages/dateutil/__pycache__: +__init__.cpython-311.pyc easter.cpython-311.pyc +_common.cpython-311.pyc relativedelta.cpython-311.pyc +_version.cpython-311.pyc rrule.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dateutil/parser: +__init__.py __pycache__ _parser.py isoparser.py +./aylm_env/lib/python3.11/site-packages/dateutil/parser/__pycache__: +__init__.cpython-311.pyc isoparser.cpython-311.pyc +_parser.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dateutil/tz: +__init__.py _common.py tz.py +__pycache__ _factories.py win.py +./aylm_env/lib/python3.11/site-packages/dateutil/tz/__pycache__: +__init__.cpython-311.pyc tz.cpython-311.pyc +_common.cpython-311.pyc win.cpython-311.pyc +_factories.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/dateutil/zoneinfo: +__init__.py rebuild.py +dateutil-zoneinfo.tar.gz +./aylm_env/lib/python3.11/site-packages/fastjsonschema: +__init__.py draft04.py exceptions.py ref_resolver.py +__main__.py draft06.py generator.py version.py +__pycache__ draft07.py indent.py +./aylm_env/lib/python3.11/site-packages/fastjsonschema/__pycache__: +__init__.cpython-311.pyc exceptions.cpython-311.pyc +__main__.cpython-311.pyc generator.cpython-311.pyc +draft04.cpython-311.pyc indent.cpython-311.pyc +draft06.cpython-311.pyc ref_resolver.cpython-311.pyc +draft07.cpython-311.pyc version.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/fastjsonschema-2.21.2.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/fastjsonschema-2.21.2.dist-info/licenses: +AUTHORS LICENSE +./aylm_env/lib/python3.11/site-packages/filelock: +__init__.py _error.py _unix.py asyncio.py +__pycache__ _read_write.py _util.py py.typed +_api.py _soft.py _windows.py version.py +./aylm_env/lib/python3.11/site-packages/filelock/__pycache__: +__init__.cpython-311.pyc _unix.cpython-311.pyc +_api.cpython-311.pyc _util.cpython-311.pyc +_error.cpython-311.pyc _windows.cpython-311.pyc +_read_write.cpython-311.pyc asyncio.cpython-311.pyc +_soft.cpython-311.pyc version.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/filelock-3.24.3.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/filelock-3.24.3.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/flask: +__init__.py cli.py helpers.py sessions.py views.py +__main__.py config.py json signals.py wrappers.py +__pycache__ ctx.py logging.py templating.py +app.py debughelpers.py py.typed testing.py +blueprints.py globals.py sansio typing.py +./aylm_env/lib/python3.11/site-packages/flask/__pycache__: +__init__.cpython-311.pyc helpers.cpython-311.pyc +__main__.cpython-311.pyc logging.cpython-311.pyc +app.cpython-311.pyc sessions.cpython-311.pyc +blueprints.cpython-311.pyc signals.cpython-311.pyc +cli.cpython-311.pyc templating.cpython-311.pyc +config.cpython-311.pyc testing.cpython-311.pyc +ctx.cpython-311.pyc typing.cpython-311.pyc +debughelpers.cpython-311.pyc views.cpython-311.pyc +globals.cpython-311.pyc wrappers.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/flask/json: +__init__.py __pycache__ provider.py tag.py +./aylm_env/lib/python3.11/site-packages/flask/json/__pycache__: +__init__.cpython-311.pyc tag.cpython-311.pyc +provider.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/flask/sansio: +README.md __pycache__ app.py blueprints.py scaffold.py +./aylm_env/lib/python3.11/site-packages/flask/sansio/__pycache__: +app.cpython-311.pyc scaffold.cpython-311.pyc +blueprints.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/flask-3.1.3.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/flask-3.1.3.dist-info/licenses: +LICENSE.txt +./aylm_env/lib/python3.11/site-packages/flatbuffers: +__init__.py builder.py flexbuffers.py table.py +__pycache__ compat.py number_types.py util.py +_version.py encode.py packer.py +./aylm_env/lib/python3.11/site-packages/flatbuffers/__pycache__: +__init__.cpython-311.pyc flexbuffers.cpython-311.pyc +_version.cpython-311.pyc number_types.cpython-311.pyc +builder.cpython-311.pyc packer.cpython-311.pyc +compat.cpython-311.pyc table.cpython-311.pyc +encode.cpython-311.pyc util.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/flatbuffers-25.12.19.dist-info: +INSTALLER METADATA RECORD WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/fontTools: +__init__.py config merge svgLib unicodedata +__main__.py cu2qu misc t1Lib varLib +afmLib.py designspaceLib mtiLib tfmLib.py voltLib +agl.py encodings otlLib ttLib +annotations.py feaLib pens ttx.py +cffLib fontBuilder.py qu2cu ufoLib +colorLib help.py subset unicode.py +./aylm_env/lib/python3.11/site-packages/fontTools/cffLib: +CFF2ToCFF.py __init__.py transforms.py +CFFToCFF2.py specializer.py width.py +./aylm_env/lib/python3.11/site-packages/fontTools/colorLib: +__init__.py errors.py table_builder.py +builder.py geometry.py unbuilder.py +./aylm_env/lib/python3.11/site-packages/fontTools/config: +__init__.py +./aylm_env/lib/python3.11/site-packages/fontTools/cu2qu: +__init__.py cu2qu.cpython-311-darwin.so +__main__.py cu2qu.py +benchmark.py errors.py +cli.py ufo.py +cu2qu.c +./aylm_env/lib/python3.11/site-packages/fontTools/designspaceLib: +__init__.py __main__.py split.py statNames.py types.py +./aylm_env/lib/python3.11/site-packages/fontTools/encodings: +MacRoman.py __init__.py +StandardEncoding.py codecs.py +./aylm_env/lib/python3.11/site-packages/fontTools/feaLib: +__init__.py lexer.cpython-311-darwin.so +__main__.py lexer.py +ast.py location.py +builder.py lookupDebugInfo.py +error.py parser.py +lexer.c variableScalar.py +./aylm_env/lib/python3.11/site-packages/fontTools/merge: +__init__.py base.py layout.py tables.py util.py +__main__.py cmap.py options.py unicode.py +./aylm_env/lib/python3.11/site-packages/fontTools/misc: +__init__.py loggingTools.py +arrayTools.py macCreatorType.py +bezierTools.c macRes.py +bezierTools.cpython-311-darwin.so plistlib +bezierTools.py psCharStrings.py +classifyTools.py psLib.py +cliTools.py psOperators.py +configTools.py py23.py +cython.py roundTools.py +dictTools.py sstruct.py +eexec.py symfont.py +encodingTools.py testTools.py +enumTools.py textTools.py +etree.py timeTools.py +filenames.py transform.py +filesystem treeTools.py +fixedTools.py vector.py +intTools.py visitor.py +iterTools.py xmlReader.py +lazyTools.py xmlWriter.py +./aylm_env/lib/python3.11/site-packages/fontTools/misc/filesystem: +__init__.py _errors.py _path.py _tools.py +_base.py _info.py _subfs.py _walk.py +_copy.py _osfs.py _tempfs.py _zipfs.py +./aylm_env/lib/python3.11/site-packages/fontTools/misc/plistlib: +__init__.py py.typed +./aylm_env/lib/python3.11/site-packages/fontTools/mtiLib: +__init__.py __main__.py +./aylm_env/lib/python3.11/site-packages/fontTools/otlLib: +__init__.py error.py optimize +builder.py maxContextCalc.py +./aylm_env/lib/python3.11/site-packages/fontTools/otlLib/optimize: +__init__.py __main__.py gpos.py +./aylm_env/lib/python3.11/site-packages/fontTools/pens: +__init__.py pointPen.py +areaPen.py qtPen.py +basePen.py qu2cuPen.py +boundsPen.py quartzPen.py +cairoPen.py recordingPen.py +cocoaPen.py reportLabPen.py +cu2quPen.py reverseContourPen.py +explicitClosingLinePen.py roundingPen.py +filterPen.py statisticsPen.py +freetypePen.py svgPathPen.py +hashPointPen.py t2CharStringPen.py +momentsPen.c teePen.py +momentsPen.cpython-311-darwin.so transformPen.py +momentsPen.py ttGlyphPen.py +perimeterPen.py wxPen.py +pointInsidePen.py +./aylm_env/lib/python3.11/site-packages/fontTools/qu2cu: +__init__.py qu2cu.c +__main__.py qu2cu.cpython-311-darwin.so +benchmark.py qu2cu.py +cli.py +./aylm_env/lib/python3.11/site-packages/fontTools/subset: +__init__.py __main__.py cff.py svg.py util.py +./aylm_env/lib/python3.11/site-packages/fontTools/svgLib: +__init__.py path +./aylm_env/lib/python3.11/site-packages/fontTools/svgLib/path: +__init__.py arc.py parser.py shapes.py +./aylm_env/lib/python3.11/site-packages/fontTools/t1Lib: +__init__.py +./aylm_env/lib/python3.11/site-packages/fontTools/ttLib: +__init__.py scaleUpem.py ttFont.py +__main__.py sfnt.py ttGlyphSet.py +macUtils.py standardGlyphOrder.py ttVisitor.py +removeOverlaps.py tables woff2.py +reorderGlyphs.py ttCollection.py +./aylm_env/lib/python3.11/site-packages/fontTools/ttLib/tables: +B_A_S_E_.py S__i_l_l.py _h_d_m_x.py +BitmapGlyphMetrics.py T_S_I_B_.py _h_e_a_d.py +C_B_D_T_.py T_S_I_C_.py _h_h_e_a.py +C_B_L_C_.py T_S_I_D_.py _h_m_t_x.py +C_F_F_.py T_S_I_J_.py _k_e_r_n.py +C_F_F__2.py T_S_I_P_.py _l_c_a_r.py +C_O_L_R_.py T_S_I_S_.py _l_o_c_a.py +C_P_A_L_.py T_S_I_V_.py _l_t_a_g.py +D_S_I_G_.py T_S_I__0.py _m_a_x_p.py +D__e_b_g.py T_S_I__1.py _m_e_t_a.py +DefaultTable.py T_S_I__2.py _m_o_r_t.py +E_B_D_T_.py T_S_I__3.py _m_o_r_x.py +E_B_L_C_.py T_S_I__5.py _n_a_m_e.py +F_F_T_M_.py T_T_F_A_.py _o_p_b_d.py +F__e_a_t.py TupleVariation.py _p_o_s_t.py +G_D_E_F_.py V_A_R_C_.py _p_r_e_p.py +G_M_A_P_.py V_D_M_X_.py _p_r_o_p.py +G_P_K_G_.py V_O_R_G_.py _s_b_i_x.py +G_P_O_S_.py V_V_A_R_.py _t_r_a_k.py +G_S_U_B_.py __init__.py _v_h_e_a.py +G_V_A_R_.py _a_n_k_r.py _v_m_t_x.py +G__l_a_t.py _a_v_a_r.py asciiTable.py +G__l_o_c.py _b_s_l_n.py grUtils.py +H_V_A_R_.py _c_i_d_g.py otBase.py +J_S_T_F_.py _c_m_a_p.py otConverters.py +L_T_S_H_.py _c_v_a_r.py otData.py +M_A_T_H_.py _c_v_t.py otTables.py +M_E_T_A_.py _f_e_a_t.py otTraverse.py +M_V_A_R_.py _f_p_g_m.py sbixGlyph.py +O_S_2f_2.py _f_v_a_r.py sbixStrike.py +S_I_N_G_.py _g_a_s_p.py table_API_readme.txt +S_T_A_T_.py _g_c_i_d.py ttProgram.py +S_V_G_.py _g_l_y_f.py +S__i_l_f.py _g_v_a_r.py +./aylm_env/lib/python3.11/site-packages/fontTools/ufoLib: +__init__.py etree.py kerning.py utils.py +converters.py filenames.py plistlib.py validators.py +errors.py glifLib.py pointPen.py +./aylm_env/lib/python3.11/site-packages/fontTools/unicodedata: +Blocks.py OTTags.py Scripts.py +Mirrored.py ScriptExtensions.py __init__.py +./aylm_env/lib/python3.11/site-packages/fontTools/varLib: +__init__.py interpolatableTestStartingPoint.py +__main__.py interpolate_layout.py +avar iup.c +avarPlanner.py iup.cpython-311-darwin.so +builder.py iup.py +cff.py merger.py +errors.py models.py +featureVars.py multiVarStore.py +hvar.py mutator.py +instancer mvar.py +interpolatable.py plot.py +interpolatableHelpers.py stat.py +interpolatablePlot.py varStore.py +interpolatableTestContourOrder.py +./aylm_env/lib/python3.11/site-packages/fontTools/varLib/avar: +__init__.py build.py plan.py +__main__.py map.py unbuild.py +./aylm_env/lib/python3.11/site-packages/fontTools/varLib/instancer: +__init__.py __main__.py featureVars.py names.py solver.py +./aylm_env/lib/python3.11/site-packages/fontTools/voltLib: +__init__.py ast.py lexer.py voltToFea.py +__main__.py error.py parser.py +./aylm_env/lib/python3.11/site-packages/fonttools-4.61.1.dist-info: +INSTALLER WHEEL top_level.txt +METADATA entry_points.txt +RECORD licenses +./aylm_env/lib/python3.11/site-packages/fonttools-4.61.1.dist-info/licenses: +LICENSE LICENSE.external +./aylm_env/lib/python3.11/site-packages/fsspec: +__init__.py callbacks.py dircache.py implementations spec.py +_version.py compression.py exceptions.py json.py tests +archive.py config.py fuse.py mapping.py transaction.py +asyn.py conftest.py generic.py parquet.py utils.py +caching.py core.py gui.py registry.py +./aylm_env/lib/python3.11/site-packages/fsspec/implementations: +__init__.py dbfs.py libarchive.py +arrow.py dirfs.py local.py +asyn_wrapper.py ftp.py memory.py +cache_mapper.py gist.py reference.py +cache_metadata.py git.py sftp.py +cached.py github.py smb.py +chained.py http.py tar.py +dask.py http_sync.py webhdfs.py +data.py jupyter.py zip.py +./aylm_env/lib/python3.11/site-packages/fsspec/tests: +abstract +./aylm_env/lib/python3.11/site-packages/fsspec/tests/abstract: +__init__.py copy.py mv.py pipe.py +common.py get.py open.py put.py +./aylm_env/lib/python3.11/site-packages/fsspec-2026.2.0.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/fsspec-2026.2.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/functorch: +__init__.py compile einops +_src dim experimental +./aylm_env/lib/python3.11/site-packages/functorch/_src: +__init__.py eager_transforms vmap +aot_autograd make_functional +./aylm_env/lib/python3.11/site-packages/functorch/_src/aot_autograd: +__init__.py +./aylm_env/lib/python3.11/site-packages/functorch/_src/eager_transforms: +__init__.py +./aylm_env/lib/python3.11/site-packages/functorch/_src/make_functional: +__init__.py +./aylm_env/lib/python3.11/site-packages/functorch/_src/vmap: +__init__.py +./aylm_env/lib/python3.11/site-packages/functorch/compile: +__init__.py +./aylm_env/lib/python3.11/site-packages/functorch/dim: +__init__.py _order.py magic_trace.py +_dim_entry.py _py_inst_decoder.py op_properties.py +_enable_all_layers.py _tensor_info.py wrap_type.py +_getsetitem.py _wrap.py +./aylm_env/lib/python3.11/site-packages/functorch/einops: +__init__.py _parsing.py rearrange.py +./aylm_env/lib/python3.11/site-packages/functorch/experimental: +__init__.py control_flow.py ops.py +./aylm_env/lib/python3.11/site-packages/google: +_upb protobuf +./aylm_env/lib/python3.11/site-packages/google/_upb: +_message.abi3.so +./aylm_env/lib/python3.11/site-packages/google/protobuf: +__init__.py field_mask_pb2.py source_context_pb2.py +__pycache__ internal struct_pb2.py +any.py json_format.py symbol_database.py +any_pb2.py message.py testdata +api_pb2.py message_factory.py text_encoding.py +compiler proto.py text_format.py +descriptor.py proto_builder.py timestamp.py +descriptor_database.py proto_json.py timestamp_pb2.py +descriptor_pb2.py proto_text.py type_pb2.py +descriptor_pool.py pyext unknown_fields.py +duration.py reflection.py util +duration_pb2.py runtime_version.py wrappers_pb2.py +empty_pb2.py service_reflection.py +./aylm_env/lib/python3.11/site-packages/google/protobuf/__pycache__: +__init__.cpython-311.pyc proto_builder.cpython-311.pyc +any.cpython-311.pyc proto_json.cpython-311.pyc +any_pb2.cpython-311.pyc proto_text.cpython-311.pyc +api_pb2.cpython-311.pyc reflection.cpython-311.pyc +descriptor.cpython-311.pyc runtime_version.cpython-311.pyc +descriptor_database.cpython-311.pyc service_reflection.cpython-311.pyc +descriptor_pb2.cpython-311.pyc source_context_pb2.cpython-311.pyc +descriptor_pool.cpython-311.pyc struct_pb2.cpython-311.pyc +duration.cpython-311.pyc symbol_database.cpython-311.pyc +duration_pb2.cpython-311.pyc text_encoding.cpython-311.pyc +empty_pb2.cpython-311.pyc text_format.cpython-311.pyc +field_mask_pb2.cpython-311.pyc timestamp.cpython-311.pyc +json_format.cpython-311.pyc timestamp_pb2.cpython-311.pyc +message.cpython-311.pyc type_pb2.cpython-311.pyc +message_factory.cpython-311.pyc unknown_fields.cpython-311.pyc +proto.cpython-311.pyc wrappers_pb2.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/google/protobuf/compiler: +__init__.py __pycache__ plugin_pb2.py +./aylm_env/lib/python3.11/site-packages/google/protobuf/compiler/__pycache__: +__init__.cpython-311.pyc plugin_pb2.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/google/protobuf/internal: +__init__.py field_mask.py +__pycache__ message_listener.py +api_implementation.py python_edition_defaults.py +builder.py python_message.py +containers.py testing_refleaks.py +decoder.py type_checkers.py +encoder.py well_known_types.py +enum_type_wrapper.py wire_format.py +extension_dict.py +./aylm_env/lib/python3.11/site-packages/google/protobuf/internal/__pycache__: +__init__.cpython-311.pyc field_mask.cpython-311.pyc +api_implementation.cpython-311.pyc message_listener.cpython-311.pyc +builder.cpython-311.pyc python_edition_defaults.cpython-311.pyc +containers.cpython-311.pyc python_message.cpython-311.pyc +decoder.cpython-311.pyc testing_refleaks.cpython-311.pyc +encoder.cpython-311.pyc type_checkers.cpython-311.pyc +enum_type_wrapper.cpython-311.pyc well_known_types.cpython-311.pyc +extension_dict.cpython-311.pyc wire_format.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/google/protobuf/pyext: +__init__.py __pycache__ cpp_message.py +./aylm_env/lib/python3.11/site-packages/google/protobuf/pyext/__pycache__: +__init__.cpython-311.pyc cpp_message.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/google/protobuf/testdata: +__init__.py __pycache__ +./aylm_env/lib/python3.11/site-packages/google/protobuf/testdata/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/google/protobuf/util: +__init__.py __pycache__ +./aylm_env/lib/python3.11/site-packages/google/protobuf/util/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat: +__init__.py compression exporter.py relocation.py utils.py +__pycache__ cuda optimizers rendering.py version.py +_helper.py distributed.py profile.py strategy +./aylm_env/lib/python3.11/site-packages/gsplat/__pycache__: +__init__.cpython-311.pyc rendering.cpython-311.pyc +distributed.cpython-311.pyc utils.cpython-311.pyc +exporter.cpython-311.pyc version.cpython-311.pyc +relocation.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat/compression: +__init__.py png_compression.py +__pycache__ sort.py +./aylm_env/lib/python3.11/site-packages/gsplat/compression/__pycache__: +__init__.cpython-311.pyc sort.cpython-311.pyc +png_compression.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat/cuda: +__init__.py _torch_impl.py csrc +__pycache__ _torch_impl_2dgs.py ext.cpp +_backend.py _wrapper.py include +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/__pycache__: +__init__.cpython-311.pyc _torch_impl_2dgs.cpython-311.pyc +_torch_impl.cpython-311.pyc _wrapper.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc: +Adam.cpp QuatScaleToCovar.h +Adam.h QuatScaleToCovarCUDA.cu +AdamCUDA.cu Rasterization.cpp +Intersect.cpp Rasterization.h +Intersect.h RasterizeToIndices2DGS.cu +IntersectTile.cu RasterizeToIndices3DGS.cu +Null.cpp RasterizeToPixels2DGSBwd.cu +Null.h RasterizeToPixels2DGSFwd.cu +NullCUDA.cu RasterizeToPixels3DGSBwd.cu +Projection.cpp RasterizeToPixels3DGSFwd.cu +Projection.h RasterizeToPixelsFromWorld3DGSBwd.cu +Projection2DGS.cuh RasterizeToPixelsFromWorld3DGSFwd.cu +Projection2DGSFused.cu Relocation.cpp +Projection2DGSPacked.cu Relocation.h +ProjectionEWA3DGSFused.cu RelocationCUDA.cu +ProjectionEWA3DGSPacked.cu SphericalHarmonics.cpp +ProjectionEWASimple.cu SphericalHarmonics.h +ProjectionUT3DGSFused.cu SphericalHarmonicsCUDA.cu +QuatScaleToCovar.cpp third_party +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party: +glm +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm: +CMakeLists.txt copying.txt glm readme.md util +cmake doc manual.md test +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/cmake: +cmake_uninstall.cmake.in +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/doc: +api man.doxy manual manual.pdf theme +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/doc/api: +a00002.html +a00002_source.html +a00005_source.html +a00008_source.html +a00011_source.html +a00014_source.html +a00017_source.html +a00020_source.html +a00023_source.html +a00026_source.html +a00029_source.html +a00032_source.html +a00035_source.html +a00038_source.html +a00041.html +a00041_source.html +a00044.html +a00044_source.html +a00047.html +a00047_source.html +a00050.html +a00050_source.html +a00053.html +a00053_source.html +a00056.html +a00056_source.html +a00059.html +a00059_source.html +a00062.html +a00062_source.html +a00065.html +a00065_source.html +a00068.html +a00068_source.html +a00071.html +a00071_source.html +a00074.html +a00074_source.html +a00077.html +a00077_source.html +a00080.html +a00080_source.html +a00083.html +a00083_source.html +a00086_source.html +a00089.html +a00089_source.html +a00092.html +a00092_source.html +a00095.html +a00095_source.html +a00098.html +a00098_source.html +a00101.html +a00101_source.html +a00104.html +a00104_source.html +a00107.html +a00107_source.html +a00110.html +a00110_source.html +a00113.html +a00113_source.html +a00116.html +a00116_source.html +a00119.html +a00119_source.html +a00122.html +a00122_source.html +a00125.html +a00125_source.html +a00128.html +a00128_source.html +a00131.html +a00131_source.html +a00134.html +a00134_source.html +a00137.html +a00137_source.html +a00140.html +a00140_source.html +a00143.html +a00143_source.html +a00146.html +a00146_source.html +a00149.html +a00149_source.html +a00152.html +a00152_source.html +a00155.html +a00155_source.html +a00158.html +a00158_source.html +a00161.html +a00161_source.html +a00164.html +a00164_source.html +a00167.html +a00167_source.html +a00170.html +a00170_source.html +a00173.html +a00173_source.html +a00176.html +a00176_source.html +a00179.html +a00179_source.html +a00182.html +a00182_source.html +a00185.html +a00185_source.html +a00188_source.html +a00191.html +a00191_source.html +a00194.html +a00194_source.html +a00197.html +a00197_source.html +a00200.html +a00200_source.html +a00203.html +a00203_source.html +a00206.html +a00206_source.html +a00209.html +a00209_source.html +a00212.html +a00212_source.html +a00215.html +a00215_source.html +a00218.html +a00218_source.html +a00221.html +a00221_source.html +a00224.html +a00224_source.html +a00227.html +a00227_source.html +a00230.html +a00230_source.html +a00233.html +a00233_source.html +a00236_source.html +a00239.html +a00239_source.html +a00242.html +a00242_source.html +a00245.html +a00245_source.html +a00248.html +a00248_source.html +a00251.html +a00251_source.html +a00254.html +a00254_source.html +a00260.html +a00260_source.html +a00263.html +a00263_source.html +a00269.html +a00269_source.html +a00272.html +a00272_source.html +a00275.html +a00275_source.html +a00278.html +a00278_source.html +a00281.html +a00281_source.html +a00284.html +a00284_source.html +a00287.html +a00287_source.html +a00290.html +a00290_source.html +a00293.html +a00293_source.html +a00296.html +a00296_source.html +a00299.html +a00299_source.html +a00302_source.html +a00305.html +a00305_source.html +a00308.html +a00308_source.html +a00311.html +a00311_source.html +a00314.html +a00314_source.html +a00317.html +a00317_source.html +a00320.html +a00320_source.html +a00323.html +a00323_source.html +a00326.html +a00326_source.html +a00329.html +a00329_source.html +a00332.html +a00332_source.html +a00335.html +a00335_source.html +a00338.html +a00338_source.html +a00341.html +a00341_source.html +a00344.html +a00344_source.html +a00347.html +a00347_source.html +a00350.html +a00350_source.html +a00353.html +a00353_source.html +a00356.html +a00356_source.html +a00359.html +a00359_source.html +a00362.html +a00362_source.html +a00365.html +a00365_source.html +a00368.html +a00368_source.html +a00374.html +a00374_source.html +a00377.html +a00377_source.html +a00380.html +a00380_source.html +a00383.html +a00383_source.html +a00386.html +a00386_source.html +a00389.html +a00389_source.html +a00392.html +a00392_source.html +a00395.html +a00395_source.html +a00398.html +a00398_source.html +a00401.html +a00401_source.html +a00404.html +a00404_source.html +a00407.html +a00407_source.html +a00410.html +a00410_source.html +a00413.html +a00413_source.html +a00416.html +a00416_source.html +a00419.html +a00419_source.html +a00422.html +a00422_source.html +a00425.html +a00425_source.html +a00428.html +a00428_source.html +a00431.html +a00431_source.html +a00434.html +a00434_source.html +a00437.html +a00437_source.html +a00440.html +a00440_source.html +a00443.html +a00443_source.html +a00446.html +a00446_source.html +a00449.html +a00449_source.html +a00452.html +a00452_source.html +a00455.html +a00455_source.html +a00458.html +a00458_source.html +a00461.html +a00461_source.html +a00464.html +a00464_source.html +a00467.html +a00467_source.html +a00470.html +a00470_source.html +a00473.html +a00473_source.html +a00476.html +a00476_source.html +a00479.html +a00479_source.html +a00482.html +a00482_source.html +a00485.html +a00485_source.html +a00488.html +a00488_source.html +a00491.html +a00491_source.html +a00494.html +a00494_source.html +a00497.html +a00497_source.html +a00500.html +a00500_source.html +a00503.html +a00503_source.html +a00506.html +a00506_source.html +a00509.html +a00509_source.html +a00512.html +a00512_source.html +a00515.html +a00515_source.html +a00518.html +a00518_source.html +a00521_source.html +a00524.html +a00524_source.html +a00527_source.html +a00530.html +a00530_source.html +a00536.html +a00536_source.html +a00539.html +a00539_source.html +a00542.html +a00542_source.html +a00545.html +a00545_source.html +a00548.html +a00548_source.html +a00551.html +a00551_source.html +a00554.html +a00554_source.html +a00560.html +a00560_source.html +a00563.html +a00563_source.html +a00566.html +a00566_source.html +a00572.html +a00572_source.html +a00575.html +a00575_source.html +a00578.html +a00578_source.html +a00581.html +a00581_source.html +a00584.html +a00584_source.html +a00587.html +a00587_source.html +a00590.html +a00590_source.html +a00593.html +a00593_source.html +a00596.html +a00596_source.html +a00599.html +a00599_source.html +a00602.html +a00602_source.html +a00605.html +a00605_source.html +a00608.html +a00608_source.html +a00611.html +a00611_source.html +a00614.html +a00614_source.html +a00617.html +a00617_source.html +a00620.html +a00620_source.html +a00623.html +a00623_source.html +a00626.html +a00626_source.html +a00629.html +a00629_source.html +a00632.html +a00632_source.html +a00635.html +a00635_source.html +a00638.html +a00638_source.html +a00641.html +a00641_source.html +a00644.html +a00644_source.html +a00647.html +a00647_source.html +a00650.html +a00650_source.html +a00653.html +a00653_source.html +a00656.html +a00656_source.html +a00659.html +a00659_source.html +a00662.html +a00662_source.html +a00665.html +a00665_source.html +a00668.html +a00668_source.html +a00671.html +a00671_source.html +a00674.html +a00674_source.html +a00677.html +a00677_source.html +a00680.html +a00680_source.html +a00683.html +a00683_source.html +a00686.html +a00686_source.html +a00689.html +a00689_source.html +a00692.html +a00692_source.html +a00695.html +a00695_source.html +a00698.html +a00698_source.html +a00701.html +a00701_source.html +a00704.html +a00704_source.html +a00707.html +a00707_source.html +a00710.html +a00710_source.html +a00713.html +a00713_source.html +a00716.html +a00716_source.html +a00719.html +a00719_source.html +a00722.html +a00722_source.html +a00725.html +a00725_source.html +a00728.html +a00728_source.html +a00731.html +a00731_source.html +a00734.html +a00734_source.html +a00737.html +a00737_source.html +a00740.html +a00740_source.html +a00743.html +a00743_source.html +a00746.html +a00746_source.html +a00749.html +a00749_source.html +a00752.html +a00752_source.html +a00755.html +a00755_source.html +a00758.html +a00758_source.html +a00761.html +a00761_source.html +a00764.html +a00764_source.html +a00767.html +a00767_source.html +a00770.html +a00770_source.html +a00773.html +a00773_source.html +a00776.html +a00776_source.html +a00779.html +a00779_source.html +a00782.html +a00782_source.html +a00785.html +a00785_source.html +a00788.html +a00788_source.html +a00791.html +a00791_source.html +a00794.html +a00794_source.html +a00797.html +a00797_source.html +a00800_source.html +a00803.html +a00804.html +a00805.html +a00806.html +a00807.html +a00808.html +a00809.html +a00810.html +a00811.html +a00812.html +a00813.html +a00814.html +a00815.html +a00816.html +a00817.html +a00818.html +a00819.html +a00820.html +a00821.html +a00822.html +a00823.html +a00824.html +a00825.html +a00826.html +a00827.html +a00828.html +a00829.html +a00830.html +a00831.html +a00832.html +a00833.html +a00834.html +a00835.html +a00836.html +a00837.html +a00838.html +a00839.html +a00840.html +a00841.html +a00842.html +a00843.html +a00844.html +a00845.html +a00846.html +a00847.html +a00848.html +a00849.html +a00850.html +a00851.html +a00852.html +a00853.html +a00854.html +a00855.html +a00856.html +a00857.html +a00858.html +a00859.html +a00860.html +a00861.html +a00862.html +a00863.html +a00864.html +a00865.html +a00866.html +a00867.html +a00868.html +a00869.html +a00870.html +a00871.html +a00872.html +a00873.html +a00874.html +a00875.html +a00876.html +a00877.html +a00878.html +a00879.html +a00880.html +a00881.html +a00882.html +a00883.html +a00884.html +a00885.html +a00886.html +a00887.html +a00888.html +a00889.html +a00890.html +a00891.html +a00892.html +a00893.html +a00894.html +a00895.html +a00896.html +a00897.html +a00898.html +a00899.html +a00900.html +a00901.html +a00902.html +a00903.html +a00904.html +a00905.html +a00906.html +a00907.html +a00908.html +a00909.html +a00910.html +a00911.html +a00912.html +a00913.html +a00914.html +a00915.html +a00916.html +a00917.html +a00918.html +a00919.html +a00920.html +a00921.html +a00922.html +a00923.html +a00924.html +a00925.html +a00926.html +a00927.html +a00928.html +a00929.html +a00930.html +a00931.html +a00932.html +a00933.html +a00934.html +a00935.html +a00936.html +a00937.html +a00938.html +a00939.html +a00940.html +a00941.html +a00942.html +a00943.html +a00944.html +a00945.html +a00946.html +a00947.html +a00948.html +a00949.html +a00950.html +a00951.html +a00952.html +a00953.html +a00954.html +a00955.html +a00956.html +a00957.html +a00958.html +a00959.html +a00960.html +a00961.html +a00962.html +a00963.html +a00964.html +a00965.html +a00966.html +a00967.html +a00968.html +a00969.html +a00970.html +a00971.html +a00972.html +a00973.html +a00974.html +a00975.html +a00976.html +a00977.html +a00978.html +a00979.html +a00980.html +a00981.html +a00982.html +a00983.html +a00984.html +a00985.html +a01558.html +a01558_source.html +a01561.html +a01561_source.html +a01564.html +a01564_source.html +a01567.html +a01567_source.html +a01570.html +a01570_source.html +a01573.html +a01573_source.html +a01576.html +a01576_source.html +a01579.html +a01579_source.html +a01582.html +a01582_source.html +a01585.html +a01585_source.html +a01588.html +a01588_source.html +a01591.html +a01591_source.html +a01594.html +a01594_source.html +a01597.html +a01597_source.html +a01600.html +a01600_source.html +a01603.html +a01603_source.html +a01606.html +a01606_source.html +arrowdown.png +arrowright.png +bc_s.png +bdwn.png +closed.png +dir_382f4fd018b81be8753cb53c0a41a09a.html +dir_50f12b6ceb23d7f6adfb377a1ae8b006.html +dir_628fd60eb37daf5aa9a81e3983c640b7.html +dir_63ed049134a778d525e06b63afc2c979.html +dir_885cc87fac2d91e269af0a5a959fa5f6.html +dir_b11711034def6a4ce452fe9c451dd3d0.html +dir_b171cecbb853a9ee4caace490047c53f.html +dir_c98a9ac98258ab9f831b188d66361a70.html +dir_d9678a6a9012da969e0b059b39347945.html +dir_fca33f1b5115d46f42c670590789c0d2.html +doc.png +doxygen.css +doxygen.png +dynsections.js +files.html +folderclosed.png +folderopen.png +index.html +jquery.js +logo-mini.png +menu.js +menudata.js +modules.html +nav_f.png +nav_g.png +nav_h.png +open.png +search +splitbar.png +sync_off.png +sync_on.png +tab_a.png +tab_b.png +tab_h.png +tab_s.png +tabs.css +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/doc/api/search: +all_0.html files_4.html functions_d.js +all_0.js files_4.js functions_e.html +all_1.html files_5.html functions_e.js +all_1.js files_5.js functions_f.html +all_10.html files_6.html functions_f.js +all_10.js files_6.js groups_0.html +all_11.html files_7.html groups_0.js +all_11.js files_7.js groups_1.html +all_12.html files_8.html groups_1.js +all_12.js files_8.js groups_2.html +all_13.html files_9.html groups_2.js +all_13.js files_9.js groups_3.html +all_14.html files_a.html groups_3.js +all_14.js files_a.js groups_4.html +all_15.html files_b.html groups_4.js +all_15.js files_b.js groups_5.html +all_16.html files_c.html groups_5.js +all_16.js files_c.js groups_6.html +all_2.html files_d.html groups_6.js +all_2.js files_d.js groups_7.html +all_3.html files_e.html groups_7.js +all_3.js files_e.js groups_8.html +all_4.html files_f.html groups_8.js +all_4.js files_f.js groups_9.html +all_5.html functions_0.html groups_9.js +all_5.js functions_0.js mag_sel.png +all_6.html functions_1.html nomatches.html +all_6.js functions_1.js pages_0.html +all_7.html functions_10.html pages_0.js +all_7.js functions_10.js search.css +all_8.html functions_11.html search.js +all_8.js functions_11.js search_l.png +all_9.html functions_12.html search_m.png +all_9.js functions_12.js search_r.png +all_a.html functions_13.html searchdata.js +all_a.js functions_13.js typedefs_0.html +all_b.html functions_14.html typedefs_0.js +all_b.js functions_14.js typedefs_1.html +all_c.html functions_15.html typedefs_1.js +all_c.js functions_15.js typedefs_2.html +all_d.html functions_16.html typedefs_2.js +all_d.js functions_16.js typedefs_3.html +all_e.html functions_2.html typedefs_3.js +all_e.js functions_2.js typedefs_4.html +all_f.html functions_3.html typedefs_4.js +all_f.js functions_3.js typedefs_5.html +close.png functions_4.html typedefs_5.js +files_0.html functions_4.js typedefs_6.html +files_0.js functions_5.html typedefs_6.js +files_1.html functions_5.js typedefs_7.html +files_1.js functions_6.html typedefs_7.js +files_10.html functions_6.js typedefs_8.html +files_10.js functions_7.html typedefs_8.js +files_11.html functions_7.js typedefs_9.html +files_11.js functions_8.html typedefs_9.js +files_12.html functions_8.js typedefs_a.html +files_12.js functions_9.html typedefs_a.js +files_13.html functions_9.js typedefs_b.html +files_13.js functions_a.html typedefs_b.js +files_14.html functions_a.js typedefs_c.html +files_14.js functions_b.html typedefs_c.js +files_2.html functions_b.js typedefs_d.html +files_2.js functions_c.html typedefs_d.js +files_3.html functions_c.js +files_3.js functions_d.html +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/doc/manual: +frontpage1.png random-circularrand.png +frontpage2.png random-diskrand.png +g-truc.png random-gaussrand.png +logo-mini.png random-linearrand.png +logo.png random-sphericalrand.png +noise-perlin1.jpg references-cinder.png +noise-perlin2.jpg references-glsl4book.jpg +noise-perlin3.jpg references-leosfortune.jpeg +noise-perlin4.png references-leosfortune2.jpg +noise-perlin5.png references-opencloth1.png +noise-perlin6.png references-opencloth3.png +noise-simplex1.jpg references-outerra1.jpg +noise-simplex2.jpg references-outerra2.jpg +noise-simplex3.jpg references-outerra3.jpg +random-ballrand.png references-outerra4.jpg +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/doc/theme: +bc_s.png folderopen.png sync_off.png +bdwn.png logo-mini.png sync_on.png +closed.png nav_f.png tab_a.png +doc.png nav_g.png tab_b.png +doxygen.css nav_h.png tab_h.png +doxygen.png open.png tab_s.png +folderclosed.png splitbar.png +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm: +CMakeLists.txt gtc mat4x3.hpp +common.hpp gtx mat4x4.hpp +detail integer.hpp matrix.hpp +exponential.hpp mat2x2.hpp packing.hpp +ext mat2x3.hpp simd +ext.hpp mat2x4.hpp trigonometric.hpp +fwd.hpp mat3x2.hpp vec2.hpp +geometric.hpp mat3x3.hpp vec3.hpp +glm.cppm mat3x4.hpp vec4.hpp +glm.hpp mat4x2.hpp vector_relational.hpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm/detail: +_features.hpp type_mat2x2.hpp +_fixes.hpp type_mat2x2.inl +_noise.hpp type_mat2x3.hpp +_swizzle.hpp type_mat2x3.inl +_swizzle_func.hpp type_mat2x4.hpp +_vectorize.hpp type_mat2x4.inl +compute_common.hpp type_mat3x2.hpp +compute_vector_decl.hpp type_mat3x2.inl +compute_vector_relational.hpp type_mat3x3.hpp +func_common.inl type_mat3x3.inl +func_common_simd.inl type_mat3x4.hpp +func_exponential.inl type_mat3x4.inl +func_exponential_simd.inl type_mat4x2.hpp +func_geometric.inl type_mat4x2.inl +func_geometric_simd.inl type_mat4x3.hpp +func_integer.inl type_mat4x3.inl +func_integer_simd.inl type_mat4x4.hpp +func_matrix.inl type_mat4x4.inl +func_matrix_simd.inl type_mat4x4_simd.inl +func_packing.inl type_quat.hpp +func_packing_simd.inl type_quat.inl +func_trigonometric.inl type_quat_simd.inl +func_trigonometric_simd.inl type_vec1.hpp +func_vector_relational.inl type_vec1.inl +func_vector_relational_simd.inl type_vec2.hpp +glm.cpp type_vec2.inl +qualifier.hpp type_vec3.hpp +setup.hpp type_vec3.inl +type_float.hpp type_vec4.hpp +type_half.hpp type_vec4.inl +type_half.inl type_vec_simd.inl +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm/ext: +_matrix_vectorize.hpp quaternion_common.hpp +matrix_clip_space.hpp quaternion_common.inl +matrix_clip_space.inl quaternion_common_simd.inl +matrix_common.hpp quaternion_double.hpp +matrix_common.inl quaternion_double_precision.hpp +matrix_double2x2.hpp quaternion_exponential.hpp +matrix_double2x2_precision.hpp quaternion_exponential.inl +matrix_double2x3.hpp quaternion_float.hpp +matrix_double2x3_precision.hpp quaternion_float_precision.hpp +matrix_double2x4.hpp quaternion_geometric.hpp +matrix_double2x4_precision.hpp quaternion_geometric.inl +matrix_double3x2.hpp quaternion_relational.hpp +matrix_double3x2_precision.hpp quaternion_relational.inl +matrix_double3x3.hpp quaternion_transform.hpp +matrix_double3x3_precision.hpp quaternion_transform.inl +matrix_double3x4.hpp quaternion_trigonometric.hpp +matrix_double3x4_precision.hpp quaternion_trigonometric.inl +matrix_double4x2.hpp scalar_common.hpp +matrix_double4x2_precision.hpp scalar_common.inl +matrix_double4x3.hpp scalar_constants.hpp +matrix_double4x3_precision.hpp scalar_constants.inl +matrix_double4x4.hpp scalar_int_sized.hpp +matrix_double4x4_precision.hpp scalar_integer.hpp +matrix_float2x2.hpp scalar_integer.inl +matrix_float2x2_precision.hpp scalar_packing.hpp +matrix_float2x3.hpp scalar_packing.inl +matrix_float2x3_precision.hpp scalar_reciprocal.hpp +matrix_float2x4.hpp scalar_reciprocal.inl +matrix_float2x4_precision.hpp scalar_relational.hpp +matrix_float3x2.hpp scalar_relational.inl +matrix_float3x2_precision.hpp scalar_uint_sized.hpp +matrix_float3x3.hpp scalar_ulp.hpp +matrix_float3x3_precision.hpp scalar_ulp.inl +matrix_float3x4.hpp vector_bool1.hpp +matrix_float3x4_precision.hpp vector_bool1_precision.hpp +matrix_float4x2.hpp vector_bool2.hpp +matrix_float4x2_precision.hpp vector_bool2_precision.hpp +matrix_float4x3.hpp vector_bool3.hpp +matrix_float4x3_precision.hpp vector_bool3_precision.hpp +matrix_float4x4.hpp vector_bool4.hpp +matrix_float4x4_precision.hpp vector_bool4_precision.hpp +matrix_int2x2.hpp vector_common.hpp +matrix_int2x2_sized.hpp vector_common.inl +matrix_int2x3.hpp vector_double1.hpp +matrix_int2x3_sized.hpp vector_double1_precision.hpp +matrix_int2x4.hpp vector_double2.hpp +matrix_int2x4_sized.hpp vector_double2_precision.hpp +matrix_int3x2.hpp vector_double3.hpp +matrix_int3x2_sized.hpp vector_double3_precision.hpp +matrix_int3x3.hpp vector_double4.hpp +matrix_int3x3_sized.hpp vector_double4_precision.hpp +matrix_int3x4.hpp vector_float1.hpp +matrix_int3x4_sized.hpp vector_float1_precision.hpp +matrix_int4x2.hpp vector_float2.hpp +matrix_int4x2_sized.hpp vector_float2_precision.hpp +matrix_int4x3.hpp vector_float3.hpp +matrix_int4x3_sized.hpp vector_float3_precision.hpp +matrix_int4x4.hpp vector_float4.hpp +matrix_int4x4_sized.hpp vector_float4_precision.hpp +matrix_integer.hpp vector_int1.hpp +matrix_integer.inl vector_int1_sized.hpp +matrix_projection.hpp vector_int2.hpp +matrix_projection.inl vector_int2_sized.hpp +matrix_relational.hpp vector_int3.hpp +matrix_relational.inl vector_int3_sized.hpp +matrix_transform.hpp vector_int4.hpp +matrix_transform.inl vector_int4_sized.hpp +matrix_uint2x2.hpp vector_integer.hpp +matrix_uint2x2_sized.hpp vector_integer.inl +matrix_uint2x3.hpp vector_packing.hpp +matrix_uint2x3_sized.hpp vector_packing.inl +matrix_uint2x4.hpp vector_reciprocal.hpp +matrix_uint2x4_sized.hpp vector_reciprocal.inl +matrix_uint3x2.hpp vector_relational.hpp +matrix_uint3x2_sized.hpp vector_relational.inl +matrix_uint3x3.hpp vector_uint1.hpp +matrix_uint3x3_sized.hpp vector_uint1_sized.hpp +matrix_uint3x4.hpp vector_uint2.hpp +matrix_uint3x4_sized.hpp vector_uint2_sized.hpp +matrix_uint4x2.hpp vector_uint3.hpp +matrix_uint4x2_sized.hpp vector_uint3_sized.hpp +matrix_uint4x3.hpp vector_uint4.hpp +matrix_uint4x3_sized.hpp vector_uint4_sized.hpp +matrix_uint4x4.hpp vector_ulp.hpp +matrix_uint4x4_sized.hpp vector_ulp.inl +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm/gtc: +bitfield.hpp matrix_inverse.hpp reciprocal.hpp +bitfield.inl matrix_inverse.inl round.hpp +color_space.hpp matrix_transform.hpp round.inl +color_space.inl matrix_transform.inl type_aligned.hpp +constants.hpp noise.hpp type_precision.hpp +constants.inl noise.inl type_precision.inl +epsilon.hpp packing.hpp type_ptr.hpp +epsilon.inl packing.inl type_ptr.inl +integer.hpp quaternion.hpp ulp.hpp +integer.inl quaternion.inl ulp.inl +matrix_access.hpp quaternion_simd.inl vec1.hpp +matrix_access.inl random.hpp +matrix_integer.hpp random.inl +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm/gtx: +associated_min_max.hpp matrix_operation.hpp +associated_min_max.inl matrix_operation.inl +bit.hpp matrix_query.hpp +bit.inl matrix_query.inl +closest_point.hpp matrix_transform_2d.hpp +closest_point.inl matrix_transform_2d.inl +color_encoding.hpp mixed_product.hpp +color_encoding.inl mixed_product.inl +color_space.hpp norm.hpp +color_space.inl norm.inl +color_space_YCoCg.hpp normal.hpp +color_space_YCoCg.inl normal.inl +common.hpp normalize_dot.hpp +common.inl normalize_dot.inl +compatibility.hpp number_precision.hpp +compatibility.inl optimum_pow.hpp +component_wise.hpp optimum_pow.inl +component_wise.inl orthonormalize.hpp +dual_quaternion.hpp orthonormalize.inl +dual_quaternion.inl pca.hpp +easing.hpp pca.inl +easing.inl perpendicular.hpp +euler_angles.hpp perpendicular.inl +euler_angles.inl polar_coordinates.hpp +extend.hpp polar_coordinates.inl +extend.inl projection.hpp +extended_min_max.hpp projection.inl +extended_min_max.inl quaternion.hpp +exterior_product.hpp quaternion.inl +exterior_product.inl range.hpp +fast_exponential.hpp raw_data.hpp +fast_exponential.inl raw_data.inl +fast_square_root.hpp rotate_normalized_axis.hpp +fast_square_root.inl rotate_normalized_axis.inl +fast_trigonometry.hpp rotate_vector.hpp +fast_trigonometry.inl rotate_vector.inl +float_normalize.inl scalar_multiplication.hpp +functions.hpp scalar_relational.hpp +functions.inl scalar_relational.inl +gradient_paint.hpp spline.hpp +gradient_paint.inl spline.inl +handed_coordinate_space.hpp std_based_type.hpp +handed_coordinate_space.inl std_based_type.inl +hash.hpp string_cast.hpp +hash.inl string_cast.inl +integer.hpp texture.hpp +integer.inl texture.inl +intersect.hpp transform.hpp +intersect.inl transform.inl +io.hpp transform2.hpp +io.inl transform2.inl +log_base.hpp type_aligned.hpp +log_base.inl type_aligned.inl +matrix_cross_product.hpp type_trait.hpp +matrix_cross_product.inl type_trait.inl +matrix_decompose.hpp vec_swizzle.hpp +matrix_decompose.inl vector_angle.hpp +matrix_factorisation.hpp vector_angle.inl +matrix_factorisation.inl vector_query.hpp +matrix_interpolation.hpp vector_query.inl +matrix_interpolation.inl wrap.hpp +matrix_major_storage.hpp wrap.inl +matrix_major_storage.inl +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/glm/simd: +common.h matrix.h trigonometric.h +exponential.h neon.h vector_relational.h +geometric.h packing.h +integer.h platform.h +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test: +CMakeLists.txt cmake ext gtc perf +bug core glm.cppcheck gtx +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/bug: +CMakeLists.txt bug_ms_vec_static.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/cmake: +CMakeLists.txt test_find_glm.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/core: +CMakeLists.txt core_func_trigonometric.cpp +core_cpp_constexpr.cpp core_func_vector_relational.cpp +core_cpp_defaulted_ctor.cpp core_setup_force_cxx03.cpp +core_force_aligned_gentypes.cpp core_setup_force_cxx98.cpp +core_force_arch_unknown.cpp core_setup_force_cxx_unknown.cpp +core_force_compiler_unknown.cpp core_setup_force_size_t_length.cpp +core_force_ctor_init.cpp core_setup_message.cpp +core_force_depth_zero_to_one.cpp core_setup_platform_unknown.cpp +core_force_explicit_ctor.cpp core_setup_precision.cpp +core_force_inline.cpp core_type_aligned.cpp +core_force_left_handed.cpp core_type_cast.cpp +core_force_platform_unknown.cpp core_type_ctor.cpp +core_force_pure.cpp core_type_int.cpp +core_force_quat_wxyz.cpp core_type_length.cpp +core_force_size_t_length.cpp core_type_mat2x2.cpp +core_force_unrestricted_gentype.cpp core_type_mat2x3.cpp +core_force_xyzw_only.cpp core_type_mat2x4.cpp +core_func_common.cpp core_type_mat3x2.cpp +core_func_exponential.cpp core_type_mat3x3.cpp +core_func_geometric.cpp core_type_mat3x4.cpp +core_func_integer.cpp core_type_mat4x2.cpp +core_func_integer_bit_count.cpp core_type_mat4x3.cpp +core_func_integer_find_lsb.cpp core_type_mat4x4.cpp +core_func_integer_find_msb.cpp core_type_vec1.cpp +core_func_matrix.cpp core_type_vec2.cpp +core_func_noise.cpp core_type_vec3.cpp +core_func_packing.cpp core_type_vec4.cpp +core_func_swizzle.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/ext: +CMakeLists.txt ext_quaternion_trigonometric.cpp +ext_matrix_clip_space.cpp ext_quaternion_type.cpp +ext_matrix_common.cpp ext_scalar_common.cpp +ext_matrix_int2x2_sized.cpp ext_scalar_constants.cpp +ext_matrix_int2x3_sized.cpp ext_scalar_int_sized.cpp +ext_matrix_int2x4_sized.cpp ext_scalar_integer.cpp +ext_matrix_int3x2_sized.cpp ext_scalar_packing.cpp +ext_matrix_int3x3_sized.cpp ext_scalar_reciprocal.cpp +ext_matrix_int3x4_sized.cpp ext_scalar_relational.cpp +ext_matrix_int4x2_sized.cpp ext_scalar_uint_sized.cpp +ext_matrix_int4x3_sized.cpp ext_scalar_ulp.cpp +ext_matrix_int4x4_sized.cpp ext_vec1.cpp +ext_matrix_integer.cpp ext_vector_bool1.cpp +ext_matrix_projection.cpp ext_vector_common.cpp +ext_matrix_relational.cpp ext_vector_iec559.cpp +ext_matrix_transform.cpp ext_vector_int1_sized.cpp +ext_matrix_uint2x2_sized.cpp ext_vector_int2_sized.cpp +ext_matrix_uint2x3_sized.cpp ext_vector_int3_sized.cpp +ext_matrix_uint2x4_sized.cpp ext_vector_int4_sized.cpp +ext_matrix_uint3x2_sized.cpp ext_vector_integer.cpp +ext_matrix_uint3x3_sized.cpp ext_vector_integer_sized.cpp +ext_matrix_uint3x4_sized.cpp ext_vector_packing.cpp +ext_matrix_uint4x2_sized.cpp ext_vector_reciprocal.cpp +ext_matrix_uint4x3_sized.cpp ext_vector_relational.cpp +ext_matrix_uint4x4_sized.cpp ext_vector_uint1_sized.cpp +ext_quaternion_common.cpp ext_vector_uint2_sized.cpp +ext_quaternion_exponential.cpp ext_vector_uint3_sized.cpp +ext_quaternion_geometric.cpp ext_vector_uint4_sized.cpp +ext_quaternion_relational.cpp ext_vector_ulp.cpp +ext_quaternion_transform.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/gtc: +CMakeLists.txt gtc_packing.cpp +gtc_bitfield.cpp gtc_quaternion.cpp +gtc_color_space.cpp gtc_random.cpp +gtc_constants.cpp gtc_reciprocal.cpp +gtc_epsilon.cpp gtc_round.cpp +gtc_integer.cpp gtc_type_aligned.cpp +gtc_matrix_access.cpp gtc_type_precision.cpp +gtc_matrix_integer.cpp gtc_type_ptr.cpp +gtc_matrix_inverse.cpp gtc_ulp.cpp +gtc_matrix_transform.cpp gtc_user_defined_types.cpp +gtc_noise.cpp gtc_vec1.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/gtx: +CMakeLists.txt gtx_matrix_interpolation.cpp +gtx.cpp gtx_matrix_major_storage.cpp +gtx_associated_min_max.cpp gtx_matrix_operation.cpp +gtx_closest_point.cpp gtx_matrix_query.cpp +gtx_color_encoding.cpp gtx_matrix_transform_2d.cpp +gtx_color_space.cpp gtx_mixed_product.cpp +gtx_color_space_YCoCg.cpp gtx_norm.cpp +gtx_common.cpp gtx_normal.cpp +gtx_compatibility.cpp gtx_normalize_dot.cpp +gtx_component_wise.cpp gtx_optimum_pow.cpp +gtx_dual_quaternion.cpp gtx_orthonormalize.cpp +gtx_easing.cpp gtx_pca.cpp +gtx_euler_angle.cpp gtx_perpendicular.cpp +gtx_extend.cpp gtx_polar_coordinates.cpp +gtx_extended_min_max.cpp gtx_projection.cpp +gtx_extented_min_max.cpp gtx_quaternion.cpp +gtx_exterior_product.cpp gtx_random.cpp +gtx_fast_exponential.cpp gtx_range.cpp +gtx_fast_square_root.cpp gtx_rotate_normalized_axis.cpp +gtx_fast_trigonometry.cpp gtx_rotate_vector.cpp +gtx_functions.cpp gtx_scalar_multiplication.cpp +gtx_gradient_paint.cpp gtx_scalar_relational.cpp +gtx_handed_coordinate_space.cpp gtx_simd_mat4.cpp +gtx_hash.cpp gtx_simd_vec4.cpp +gtx_int_10_10_10_2.cpp gtx_spline.cpp +gtx_integer.cpp gtx_string_cast.cpp +gtx_intersect.cpp gtx_texture.cpp +gtx_io.cpp gtx_type_aligned.cpp +gtx_load.cpp gtx_type_trait.cpp +gtx_log_base.cpp gtx_vec_swizzle.cpp +gtx_matrix_cross_product.cpp gtx_vector_angle.cpp +gtx_matrix_decompose.cpp gtx_vector_query.cpp +gtx_matrix_factorisation.cpp gtx_wrap.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/test/perf: +CMakeLists.txt perf_matrix_mul_vector.cpp +perf_matrix_div.cpp perf_matrix_transpose.cpp +perf_matrix_inverse.cpp perf_vector_mul_matrix.cpp +perf_matrix_mul.cpp +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/csrc/third_party/glm/util: +autoexp.txt glm.natvis +autoexp.vc2010.dat usertype.dat +./aylm_env/lib/python3.11/site-packages/gsplat/cuda/include: +Cameras.cuh Cameras.h Common.h Ops.h Utils.cuh +./aylm_env/lib/python3.11/site-packages/gsplat/optimizers: +__init__.py __pycache__ selective_adam.py +./aylm_env/lib/python3.11/site-packages/gsplat/optimizers/__pycache__: +__init__.cpython-311.pyc selective_adam.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat/strategy: +__init__.py base.py mcmc.py +__pycache__ default.py ops.py +./aylm_env/lib/python3.11/site-packages/gsplat/strategy/__pycache__: +__init__.cpython-311.pyc mcmc.cpython-311.pyc +base.cpython-311.pyc ops.cpython-311.pyc +default.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/gsplat-1.5.3.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/gsplat-1.5.3.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/h11: +__init__.py _headers.py _version.py +__pycache__ _readers.py _writers.py +_abnf.py _receivebuffer.py py.typed +_connection.py _state.py +_events.py _util.py +./aylm_env/lib/python3.11/site-packages/h11/__pycache__: +__init__.cpython-311.pyc _receivebuffer.cpython-311.pyc +_abnf.cpython-311.pyc _state.cpython-311.pyc +_connection.cpython-311.pyc _util.cpython-311.pyc +_events.cpython-311.pyc _version.cpython-311.pyc +_headers.cpython-311.pyc _writers.cpython-311.pyc +_readers.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/h11-0.16.0.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/h11-0.16.0.dist-info/licenses: +LICENSE.txt +./aylm_env/lib/python3.11/site-packages/hf_xet: +__init__.py hf_xet.abi3.so +./aylm_env/lib/python3.11/site-packages/hf_xet-1.2.0.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/hf_xet-1.2.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/httpcore: +__init__.py _exceptions.py _trace.py +__pycache__ _models.py _utils.py +_api.py _ssl.py py.typed +_async _sync +_backends _synchronization.py +./aylm_env/lib/python3.11/site-packages/httpcore/__pycache__: +__init__.cpython-311.pyc _ssl.cpython-311.pyc +_api.cpython-311.pyc _synchronization.cpython-311.pyc +_exceptions.cpython-311.pyc _trace.cpython-311.pyc +_models.cpython-311.pyc _utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpcore/_async: +__init__.py connection_pool.py http_proxy.py +__pycache__ http11.py interfaces.py +connection.py http2.py socks_proxy.py +./aylm_env/lib/python3.11/site-packages/httpcore/_async/__pycache__: +__init__.cpython-311.pyc http2.cpython-311.pyc +connection.cpython-311.pyc http_proxy.cpython-311.pyc +connection_pool.cpython-311.pyc interfaces.cpython-311.pyc +http11.cpython-311.pyc socks_proxy.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpcore/_backends: +__init__.py anyio.py base.py sync.py +__pycache__ auto.py mock.py trio.py +./aylm_env/lib/python3.11/site-packages/httpcore/_backends/__pycache__: +__init__.cpython-311.pyc mock.cpython-311.pyc +anyio.cpython-311.pyc sync.cpython-311.pyc +auto.cpython-311.pyc trio.cpython-311.pyc +base.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpcore/_sync: +__init__.py connection_pool.py http_proxy.py +__pycache__ http11.py interfaces.py +connection.py http2.py socks_proxy.py +./aylm_env/lib/python3.11/site-packages/httpcore/_sync/__pycache__: +__init__.cpython-311.pyc http2.cpython-311.pyc +connection.cpython-311.pyc http_proxy.cpython-311.pyc +connection_pool.cpython-311.pyc interfaces.cpython-311.pyc +http11.cpython-311.pyc socks_proxy.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpcore-1.0.9.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/httpcore-1.0.9.dist-info/licenses: +LICENSE.md +./aylm_env/lib/python3.11/site-packages/httpx: +__init__.py _content.py _transports +__pycache__ _decoders.py _types.py +__version__.py _exceptions.py _urlparse.py +_api.py _main.py _urls.py +_auth.py _models.py _utils.py +_client.py _multipart.py py.typed +_config.py _status_codes.py +./aylm_env/lib/python3.11/site-packages/httpx/__pycache__: +__init__.cpython-311.pyc _main.cpython-311.pyc +__version__.cpython-311.pyc _models.cpython-311.pyc +_api.cpython-311.pyc _multipart.cpython-311.pyc +_auth.cpython-311.pyc _status_codes.cpython-311.pyc +_client.cpython-311.pyc _types.cpython-311.pyc +_config.cpython-311.pyc _urlparse.cpython-311.pyc +_content.cpython-311.pyc _urls.cpython-311.pyc +_decoders.cpython-311.pyc _utils.cpython-311.pyc +_exceptions.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpx/_transports: +__init__.py asgi.py default.py wsgi.py +__pycache__ base.py mock.py +./aylm_env/lib/python3.11/site-packages/httpx/_transports/__pycache__: +__init__.cpython-311.pyc default.cpython-311.pyc +asgi.cpython-311.pyc mock.cpython-311.pyc +base.cpython-311.pyc wsgi.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/httpx-0.28.1.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/httpx-0.28.1.dist-info/licenses: +LICENSE.md +./aylm_env/lib/python3.11/site-packages/huggingface_hub: +__init__.py _tensorboard_logger.py hf_file_system.py +__pycache__ _upload_large_folder.py hub_mixin.py +_commit_api.py _webhooks_payload.py inference +_commit_scheduler.py _webhooks_server.py lfs.py +_eval_results.py cli py.typed +_inference_endpoints.py community.py repocard.py +_jobs_api.py constants.py repocard_data.py +_local_folder.py dataclasses.py serialization +_login.py errors.py templates +_oauth.py fastai_utils.py utils +_snapshot_download.py file_download.py +_space_api.py hf_api.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/__pycache__: +__init__.cpython-311.pyc community.cpython-311.pyc +_commit_api.cpython-311.pyc constants.cpython-311.pyc +_eval_results.cpython-311.pyc errors.cpython-311.pyc +_inference_endpoints.cpython-311.pyc file_download.cpython-311.pyc +_jobs_api.cpython-311.pyc hf_api.cpython-311.pyc +_local_folder.cpython-311.pyc lfs.cpython-311.pyc +_space_api.cpython-311.pyc repocard_data.cpython-311.pyc +_upload_large_folder.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/huggingface_hub/cli: +__init__.py download.py repo.py +_cli_utils.py hf.py repo_files.py +_errors.py inference_endpoints.py skills.py +auth.py jobs.py spaces.py +cache.py lfs.py system.py +collections.py models.py upload.py +datasets.py papers.py upload_large_folder.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/inference: +__init__.py _common.py _mcp +_client.py _generated _providers +./aylm_env/lib/python3.11/site-packages/huggingface_hub/inference/_generated: +__init__.py _async_client.py types +./aylm_env/lib/python3.11/site-packages/huggingface_hub/inference/_generated/types: +__init__.py question_answering.py +audio_classification.py sentence_similarity.py +audio_to_audio.py summarization.py +automatic_speech_recognition.py table_question_answering.py +base.py text2text_generation.py +chat_completion.py text_classification.py +depth_estimation.py text_generation.py +document_question_answering.py text_to_audio.py +feature_extraction.py text_to_image.py +fill_mask.py text_to_speech.py +image_classification.py text_to_video.py +image_segmentation.py token_classification.py +image_text_to_image.py translation.py +image_text_to_video.py video_classification.py +image_to_image.py visual_question_answering.py +image_to_text.py zero_shot_classification.py +image_to_video.py zero_shot_image_classification.py +object_detection.py zero_shot_object_detection.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/inference/_mcp: +__init__.py agent.py constants.py types.py +_cli_hacks.py cli.py mcp_client.py utils.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/inference/_providers: +__init__.py fireworks_ai.py ovhcloud.py +_common.py groq.py publicai.py +black_forest_labs.py hf_inference.py replicate.py +cerebras.py hyperbolic.py sambanova.py +clarifai.py nebius.py scaleway.py +cohere.py novita.py together.py +fal_ai.py nscale.py wavespeed.py +featherless_ai.py openai.py zai_org.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/serialization: +__init__.py _base.py _dduf.py _torch.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/templates: +datasetcard_template.md modelcard_template.md +./aylm_env/lib/python3.11/site-packages/huggingface_hub/utils: +__init__.py _paths.py +__pycache__ _runtime.py +_auth.py _safetensors.py +_cache_assets.py _subprocess.py +_cache_manager.py _telemetry.py +_chunk_utils.py _terminal.py +_datetime.py _typing.py +_deprecation.py _validators.py +_dotenv.py _verification.py +_experimental.py _xet.py +_fixes.py _xet_progress_reporting.py +_git_credential.py endpoint_helpers.py +_headers.py insecure_hashlib.py +_http.py logging.py +_lfs.py sha.py +_pagination.py tqdm.py +_parsing.py +./aylm_env/lib/python3.11/site-packages/huggingface_hub/utils/__pycache__: +__init__.cpython-311.pyc _paths.cpython-311.pyc +_auth.cpython-311.pyc _runtime.cpython-311.pyc +_cache_assets.cpython-311.pyc _safetensors.cpython-311.pyc +_cache_manager.cpython-311.pyc _subprocess.cpython-311.pyc +_chunk_utils.cpython-311.pyc _telemetry.cpython-311.pyc +_datetime.cpython-311.pyc _terminal.cpython-311.pyc +_deprecation.cpython-311.pyc _typing.cpython-311.pyc +_experimental.cpython-311.pyc _validators.cpython-311.pyc +_fixes.cpython-311.pyc _verification.cpython-311.pyc +_git_credential.cpython-311.pyc _xet.cpython-311.pyc +_headers.cpython-311.pyc endpoint_helpers.cpython-311.pyc +_http.cpython-311.pyc insecure_hashlib.cpython-311.pyc +_lfs.cpython-311.pyc logging.cpython-311.pyc +_pagination.cpython-311.pyc sha.cpython-311.pyc +_parsing.cpython-311.pyc tqdm.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/huggingface_hub-1.4.1.dist-info: +INSTALLER WHEEL top_level.txt +METADATA entry_points.txt +RECORD licenses +./aylm_env/lib/python3.11/site-packages/huggingface_hub-1.4.1.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/humanfriendly: +__init__.py cli.py deprecation.py tables.py tests.py +__pycache__ compat.py prompts.py terminal text.py +case.py decorators.py sphinx.py testing.py usage.py +./aylm_env/lib/python3.11/site-packages/humanfriendly/__pycache__: +__init__.cpython-311.pyc sphinx.cpython-311.pyc +case.cpython-311.pyc tables.cpython-311.pyc +cli.cpython-311.pyc testing.cpython-311.pyc +compat.cpython-311.pyc tests.cpython-311.pyc +decorators.cpython-311.pyc text.cpython-311.pyc +deprecation.cpython-311.pyc usage.cpython-311.pyc +prompts.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/humanfriendly/terminal: +__init__.py __pycache__ html.py spinners.py +./aylm_env/lib/python3.11/site-packages/humanfriendly/terminal/__pycache__: +__init__.cpython-311.pyc spinners.cpython-311.pyc +html.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/humanfriendly-10.0.dist-info: +INSTALLER RECORD top_level.txt +LICENSE.txt WHEEL +METADATA entry_points.txt +./aylm_env/lib/python3.11/site-packages/idna: +__init__.py codec.py core.py intranges.py py.typed +__pycache__ compat.py idnadata.py package_data.py uts46data.py +./aylm_env/lib/python3.11/site-packages/idna/__pycache__: +__init__.cpython-311.pyc intranges.cpython-311.pyc +core.cpython-311.pyc package_data.cpython-311.pyc +idnadata.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/idna-3.11.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/idna-3.11.dist-info/licenses: +LICENSE.md +./aylm_env/lib/python3.11/site-packages/imageio: +__init__.py config plugins typing.py v3.py +__main__.py core py.typed v2.py v3.pyi +__pycache__ freeze.py testing.py v2.pyi +./aylm_env/lib/python3.11/site-packages/imageio/__pycache__: +__init__.cpython-311.pyc v2.cpython-311.pyc +typing.cpython-311.pyc v3.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/imageio/config: +__init__.py extensions.py plugins.py +__pycache__ extensions.pyi plugins.pyi +./aylm_env/lib/python3.11/site-packages/imageio/config/__pycache__: +__init__.cpython-311.pyc plugins.cpython-311.pyc +extensions.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/imageio/core: +__init__.py imopen.pyi +__pycache__ legacy_plugin_wrapper.py +fetching.py legacy_plugin_wrapper.pyi +findlib.py request.py +format.py request.pyi +format.pyi util.py +imopen.py v3_plugin_api.py +./aylm_env/lib/python3.11/site-packages/imageio/core/__pycache__: +__init__.cpython-311.pyc legacy_plugin_wrapper.cpython-311.pyc +fetching.cpython-311.pyc request.cpython-311.pyc +findlib.cpython-311.pyc util.cpython-311.pyc +format.cpython-311.pyc v3_plugin_api.cpython-311.pyc +imopen.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/imageio/plugins: +__init__.py ffmpeg.py pillow_legacy.py +__pycache__ fits.py pillowmulti.py +_bsdf.py freeimage.py pyav.py +_dicom.py freeimagemulti.py rawpy.py +_freeimage.py gdal.py simpleitk.py +_swf.py grab.py spe.py +_tifffile.py lytro.py swf.py +bsdf.py npz.py tifffile.py +dicom.py opencv.py tifffile_v3.py +example.py pillow.py +feisem.py pillow_info.py +./aylm_env/lib/python3.11/site-packages/imageio/plugins/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/imageio-2.37.2.dist-info: +INSTALLER WHEEL top_level.txt +METADATA entry_points.txt +RECORD licenses +./aylm_env/lib/python3.11/site-packages/imageio-2.37.2.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/imageio_ffmpeg: +__init__.py _io.py _utils.py +_definitions.py _parsing.py binaries +./aylm_env/lib/python3.11/site-packages/imageio_ffmpeg/binaries: +README.md ffmpeg-macos-aarch64-v7.1 +__init__.py +./aylm_env/lib/python3.11/site-packages/imageio_ffmpeg-0.6.0.dist-info: +INSTALLER METADATA WHEEL +LICENSE RECORD top_level.txt +./aylm_env/lib/python3.11/site-packages/importlib_metadata: +__init__.py _collections.py _itertools.py _typing.py py.typed +__pycache__ _compat.py _meta.py compat +_adapters.py _functools.py _text.py diagnose.py +./aylm_env/lib/python3.11/site-packages/importlib_metadata/__pycache__: +__init__.cpython-311.pyc _itertools.cpython-311.pyc +_adapters.cpython-311.pyc _meta.cpython-311.pyc +_collections.cpython-311.pyc _text.cpython-311.pyc +_compat.cpython-311.pyc _typing.cpython-311.pyc +_functools.cpython-311.pyc diagnose.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/importlib_metadata/compat: +__init__.py __pycache__ py311.py py39.py +./aylm_env/lib/python3.11/site-packages/importlib_metadata/compat/__pycache__: +__init__.cpython-311.pyc py39.cpython-311.pyc +py311.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/importlib_metadata-8.7.1.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/importlib_metadata-8.7.1.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/iniconfig: +__init__.py _parse.py exceptions.py +__pycache__ _version.py py.typed +./aylm_env/lib/python3.11/site-packages/iniconfig/__pycache__: +__init__.cpython-311.pyc _version.cpython-311.pyc +_parse.cpython-311.pyc exceptions.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/iniconfig-2.3.0.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/iniconfig-2.3.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/isort: +__init__.py comments.py identify.py parse.py sorting.py +__main__.py core.py io.py place.py stdlibs +__pycache__ exceptions.py literal.py profiles.py utils.py +_vendored files.py logo.py py.typed wrap.py +_version.py format.py main.py sections.py wrap_modes.py +api.py hooks.py output.py settings.py +./aylm_env/lib/python3.11/site-packages/isort/__pycache__: +__init__.cpython-311.pyc main.cpython-311.pyc +_version.cpython-311.pyc output.cpython-311.pyc +api.cpython-311.pyc parse.cpython-311.pyc +comments.cpython-311.pyc place.cpython-311.pyc +core.cpython-311.pyc profiles.cpython-311.pyc +exceptions.cpython-311.pyc sections.cpython-311.pyc +files.cpython-311.pyc settings.cpython-311.pyc +format.cpython-311.pyc sorting.cpython-311.pyc +identify.cpython-311.pyc utils.cpython-311.pyc +io.cpython-311.pyc wrap.cpython-311.pyc +literal.cpython-311.pyc wrap_modes.cpython-311.pyc +logo.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/isort/_vendored: +tomli +./aylm_env/lib/python3.11/site-packages/isort/_vendored/tomli: +LICENSE __init__.py _parser.py _re.py py.typed +./aylm_env/lib/python3.11/site-packages/isort/stdlibs: +__init__.py py2.py py310.py py313.py py37.py +__pycache__ py27.py py311.py py314.py py38.py +all.py py3.py py312.py py36.py py39.py +./aylm_env/lib/python3.11/site-packages/isort/stdlibs/__pycache__: +__init__.cpython-311.pyc py312.cpython-311.pyc +all.cpython-311.pyc py313.cpython-311.pyc +py2.cpython-311.pyc py314.cpython-311.pyc +py27.cpython-311.pyc py36.cpython-311.pyc +py3.cpython-311.pyc py37.cpython-311.pyc +py310.cpython-311.pyc py38.cpython-311.pyc +py311.cpython-311.pyc py39.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/isort-8.0.0.dist-info: +INSTALLER REQUESTED licenses +METADATA WHEEL +RECORD entry_points.txt +./aylm_env/lib/python3.11/site-packages/isort-8.0.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/itsdangerous: +__init__.py _json.py exc.py serializer.py timed.py +__pycache__ encoding.py py.typed signer.py url_safe.py +./aylm_env/lib/python3.11/site-packages/itsdangerous/__pycache__: +__init__.cpython-311.pyc serializer.cpython-311.pyc +_json.cpython-311.pyc signer.cpython-311.pyc +encoding.cpython-311.pyc timed.cpython-311.pyc +exc.cpython-311.pyc url_safe.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/itsdangerous-2.2.0.dist-info: +INSTALLER LICENSE.txt METADATA RECORD WHEEL +./aylm_env/lib/python3.11/site-packages/jaxtyping: +__init__.py _errors.py _pytree_type.py +__pycache__ _import_hook.py _storage.py +_array_types.py _indirection.py _typeguard +_config.py _ipython_extension.py py.typed +_decorator.py _pytest_plugin.py +./aylm_env/lib/python3.11/site-packages/jaxtyping/__pycache__: +__init__.cpython-311-pytest-9.0.2.pyc +_array_types.cpython-311-pytest-9.0.2.pyc +_config.cpython-311-pytest-9.0.2.pyc +_decorator.cpython-311-pytest-9.0.2.pyc +_errors.cpython-311-pytest-9.0.2.pyc +_import_hook.cpython-311-pytest-9.0.2.pyc +_ipython_extension.cpython-311-pytest-9.0.2.pyc +_pytest_plugin.cpython-311-pytest-9.0.2.pyc +_storage.cpython-311-pytest-9.0.2.pyc +./aylm_env/lib/python3.11/site-packages/jaxtyping/_typeguard: +LICENSE README.md __init__.py +./aylm_env/lib/python3.11/site-packages/jaxtyping-0.3.9.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/jaxtyping-0.3.9.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/jinja2: +__init__.py constants.py filters.py nodes.py tests.py +__pycache__ debug.py idtracking.py optimizer.py utils.py +_identifier.py defaults.py lexer.py parser.py visitor.py +async_utils.py environment.py loaders.py py.typed +bccache.py exceptions.py meta.py runtime.py +compiler.py ext.py nativetypes.py sandbox.py +./aylm_env/lib/python3.11/site-packages/jinja2/__pycache__: +__init__.cpython-311.pyc lexer.cpython-311.pyc +_identifier.cpython-311.pyc loaders.cpython-311.pyc +async_utils.cpython-311.pyc nodes.cpython-311.pyc +bccache.cpython-311.pyc optimizer.cpython-311.pyc +compiler.cpython-311.pyc parser.cpython-311.pyc +defaults.cpython-311.pyc runtime.cpython-311.pyc +environment.cpython-311.pyc tests.cpython-311.pyc +exceptions.cpython-311.pyc utils.cpython-311.pyc +filters.cpython-311.pyc visitor.cpython-311.pyc +idtracking.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jinja2-3.1.6.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/jinja2-3.1.6.dist-info/licenses: +LICENSE.txt +./aylm_env/lib/python3.11/site-packages/joblib: +__init__.py externals +__pycache__ func_inspect.py +_cloudpickle_wrapper.py hashing.py +_dask.py logger.py +_memmapping_reducer.py memory.py +_multiprocessing_helpers.py numpy_pickle.py +_parallel_backends.py numpy_pickle_compat.py +_store_backends.py numpy_pickle_utils.py +_utils.py parallel.py +backports.py pool.py +compressor.py test +disk.py testing.py +executor.py +./aylm_env/lib/python3.11/site-packages/joblib/__pycache__: +__init__.cpython-311.pyc +_cloudpickle_wrapper.cpython-311.pyc +_dask.cpython-311.pyc +_memmapping_reducer.cpython-311.pyc +_multiprocessing_helpers.cpython-311.pyc +_parallel_backends.cpython-311.pyc +_store_backends.cpython-311.pyc +_utils.cpython-311.pyc +backports.cpython-311.pyc +compressor.cpython-311.pyc +disk.cpython-311.pyc +executor.cpython-311.pyc +func_inspect.cpython-311.pyc +hashing.cpython-311.pyc +logger.cpython-311.pyc +memory.cpython-311.pyc +numpy_pickle.cpython-311.pyc +numpy_pickle_compat.cpython-311.pyc +numpy_pickle_utils.cpython-311.pyc +parallel.cpython-311.pyc +pool.cpython-311.pyc +testing.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/externals: +__init__.py __pycache__ cloudpickle loky +./aylm_env/lib/python3.11/site-packages/joblib/externals/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/externals/cloudpickle: +__init__.py cloudpickle.py +__pycache__ cloudpickle_fast.py +./aylm_env/lib/python3.11/site-packages/joblib/externals/cloudpickle/__pycache__: +__init__.cpython-311.pyc cloudpickle_fast.cpython-311.pyc +cloudpickle.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/externals/loky: +__init__.py backend process_executor.py +__pycache__ cloudpickle_wrapper.py reusable_executor.py +_base.py initializers.py +./aylm_env/lib/python3.11/site-packages/joblib/externals/loky/__pycache__: +__init__.cpython-311.pyc initializers.cpython-311.pyc +_base.cpython-311.pyc process_executor.cpython-311.pyc +cloudpickle_wrapper.cpython-311.pyc reusable_executor.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/externals/loky/backend: +__init__.py fork_exec.py reduction.py +__pycache__ popen_loky_posix.py resource_tracker.py +_posix_reduction.py popen_loky_win32.py spawn.py +_win_reduction.py process.py synchronize.py +context.py queues.py utils.py +./aylm_env/lib/python3.11/site-packages/joblib/externals/loky/backend/__pycache__: +__init__.cpython-311.pyc process.cpython-311.pyc +_posix_reduction.cpython-311.pyc queues.cpython-311.pyc +_win_reduction.cpython-311.pyc reduction.cpython-311.pyc +context.cpython-311.pyc resource_tracker.cpython-311.pyc +fork_exec.cpython-311.pyc spawn.cpython-311.pyc +popen_loky_posix.cpython-311.pyc synchronize.cpython-311.pyc +popen_loky_win32.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/test: +__init__.py test_memmapping.py +__pycache__ test_memory.py +common.py test_memory_async.py +data test_missing_multiprocessing.py +test_backports.py test_module.py +test_cloudpickle_wrapper.py test_numpy_pickle.py +test_config.py test_numpy_pickle_compat.py +test_dask.py test_numpy_pickle_utils.py +test_disk.py test_parallel.py +test_func_inspect.py test_store_backends.py +test_func_inspect_special_encoding.py test_testing.py +test_hashing.py test_utils.py +test_init.py testutils.py +test_logger.py +./aylm_env/lib/python3.11/site-packages/joblib/test/__pycache__: +__init__.cpython-311.pyc +common.cpython-311.pyc +test_backports.cpython-311.pyc +test_cloudpickle_wrapper.cpython-311.pyc +test_config.cpython-311.pyc +test_dask.cpython-311.pyc +test_disk.cpython-311.pyc +test_func_inspect.cpython-311.pyc +test_func_inspect_special_encoding.cpython-311.pyc +test_hashing.cpython-311.pyc +test_init.cpython-311.pyc +test_logger.cpython-311.pyc +test_memmapping.cpython-311.pyc +test_memory.cpython-311.pyc +test_memory_async.cpython-311.pyc +test_missing_multiprocessing.cpython-311.pyc +test_module.cpython-311.pyc +test_numpy_pickle.cpython-311.pyc +test_numpy_pickle_compat.cpython-311.pyc +test_numpy_pickle_utils.cpython-311.pyc +test_parallel.cpython-311.pyc +test_store_backends.cpython-311.pyc +test_testing.cpython-311.pyc +test_utils.cpython-311.pyc +testutils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib/test/data: +__init__.py +__pycache__ +create_numpy_pickle.py +joblib_0.10.0_compressed_pickle_py27_np16.gz +joblib_0.10.0_compressed_pickle_py27_np17.gz +joblib_0.10.0_compressed_pickle_py33_np18.gz +joblib_0.10.0_compressed_pickle_py34_np19.gz +joblib_0.10.0_compressed_pickle_py35_np19.gz +joblib_0.10.0_pickle_py27_np17.pkl +joblib_0.10.0_pickle_py27_np17.pkl.bz2 +joblib_0.10.0_pickle_py27_np17.pkl.gzip +joblib_0.10.0_pickle_py27_np17.pkl.lzma +joblib_0.10.0_pickle_py27_np17.pkl.xz +joblib_0.10.0_pickle_py33_np18.pkl +joblib_0.10.0_pickle_py33_np18.pkl.bz2 +joblib_0.10.0_pickle_py33_np18.pkl.gzip +joblib_0.10.0_pickle_py33_np18.pkl.lzma +joblib_0.10.0_pickle_py33_np18.pkl.xz +joblib_0.10.0_pickle_py34_np19.pkl +joblib_0.10.0_pickle_py34_np19.pkl.bz2 +joblib_0.10.0_pickle_py34_np19.pkl.gzip +joblib_0.10.0_pickle_py34_np19.pkl.lzma +joblib_0.10.0_pickle_py34_np19.pkl.xz +joblib_0.10.0_pickle_py35_np19.pkl +joblib_0.10.0_pickle_py35_np19.pkl.bz2 +joblib_0.10.0_pickle_py35_np19.pkl.gzip +joblib_0.10.0_pickle_py35_np19.pkl.lzma +joblib_0.10.0_pickle_py35_np19.pkl.xz +joblib_0.11.0_compressed_pickle_py36_np111.gz +joblib_0.11.0_pickle_py36_np111.pkl +joblib_0.11.0_pickle_py36_np111.pkl.bz2 +joblib_0.11.0_pickle_py36_np111.pkl.gzip +joblib_0.11.0_pickle_py36_np111.pkl.lzma +joblib_0.11.0_pickle_py36_np111.pkl.xz +joblib_0.8.4_compressed_pickle_py27_np17.gz +joblib_0.9.2_compressed_pickle_py27_np16.gz +joblib_0.9.2_compressed_pickle_py27_np17.gz +joblib_0.9.2_compressed_pickle_py34_np19.gz +joblib_0.9.2_compressed_pickle_py35_np19.gz +joblib_0.9.2_pickle_py27_np16.pkl +joblib_0.9.2_pickle_py27_np16.pkl_01.npy +joblib_0.9.2_pickle_py27_np16.pkl_02.npy +joblib_0.9.2_pickle_py27_np16.pkl_03.npy +joblib_0.9.2_pickle_py27_np16.pkl_04.npy +joblib_0.9.2_pickle_py27_np17.pkl +joblib_0.9.2_pickle_py27_np17.pkl_01.npy +joblib_0.9.2_pickle_py27_np17.pkl_02.npy +joblib_0.9.2_pickle_py27_np17.pkl_03.npy +joblib_0.9.2_pickle_py27_np17.pkl_04.npy +joblib_0.9.2_pickle_py33_np18.pkl +joblib_0.9.2_pickle_py33_np18.pkl_01.npy +joblib_0.9.2_pickle_py33_np18.pkl_02.npy +joblib_0.9.2_pickle_py33_np18.pkl_03.npy +joblib_0.9.2_pickle_py33_np18.pkl_04.npy +joblib_0.9.2_pickle_py34_np19.pkl +joblib_0.9.2_pickle_py34_np19.pkl_01.npy +joblib_0.9.2_pickle_py34_np19.pkl_02.npy +joblib_0.9.2_pickle_py34_np19.pkl_03.npy +joblib_0.9.2_pickle_py34_np19.pkl_04.npy +joblib_0.9.2_pickle_py35_np19.pkl +joblib_0.9.2_pickle_py35_np19.pkl_01.npy +joblib_0.9.2_pickle_py35_np19.pkl_02.npy +joblib_0.9.2_pickle_py35_np19.pkl_03.npy +joblib_0.9.2_pickle_py35_np19.pkl_04.npy +joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz +joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_01.npy.z +joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_02.npy.z +joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_03.npy.z +./aylm_env/lib/python3.11/site-packages/joblib/test/data/__pycache__: +__init__.cpython-311.pyc create_numpy_pickle.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/joblib-1.5.3.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/joblib-1.5.3.dist-info/licenses: +LICENSE.txt +./aylm_env/lib/python3.11/site-packages/jsonschema: +__init__.py _legacy_keywords.py cli.py +__main__.py _types.py exceptions.py +__pycache__ _typing.py protocols.py +_format.py _utils.py tests +_keywords.py benchmarks validators.py +./aylm_env/lib/python3.11/site-packages/jsonschema/__pycache__: +__init__.cpython-311.pyc _typing.cpython-311.pyc +__main__.cpython-311.pyc _utils.cpython-311.pyc +_format.cpython-311.pyc cli.cpython-311.pyc +_keywords.cpython-311.pyc exceptions.cpython-311.pyc +_legacy_keywords.cpython-311.pyc protocols.cpython-311.pyc +_types.cpython-311.pyc validators.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema/benchmarks: +__init__.py json_schema_test_suite.py +__pycache__ nested_schemas.py +const_vs_enum.py subcomponents.py +contains.py unused_registry.py +import_benchmark.py useless_applicator_schemas.py +issue232 useless_keywords.py +issue232.py validator_creation.py +./aylm_env/lib/python3.11/site-packages/jsonschema/benchmarks/__pycache__: +__init__.cpython-311.pyc +const_vs_enum.cpython-311.pyc +contains.cpython-311.pyc +import_benchmark.cpython-311.pyc +issue232.cpython-311.pyc +json_schema_test_suite.cpython-311.pyc +nested_schemas.cpython-311.pyc +subcomponents.cpython-311.pyc +unused_registry.cpython-311.pyc +useless_applicator_schemas.cpython-311.pyc +useless_keywords.cpython-311.pyc +validator_creation.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema/benchmarks/issue232: +issue.json +./aylm_env/lib/python3.11/site-packages/jsonschema/tests: +__init__.py test_format.py +__pycache__ test_jsonschema_test_suite.py +_suite.py test_types.py +fuzz_validate.py test_utils.py +test_cli.py test_validators.py +test_deprecations.py typing +test_exceptions.py +./aylm_env/lib/python3.11/site-packages/jsonschema/tests/__pycache__: +__init__.cpython-311.pyc +_suite.cpython-311.pyc +fuzz_validate.cpython-311.pyc +test_cli.cpython-311.pyc +test_deprecations.cpython-311.pyc +test_exceptions.cpython-311.pyc +test_format.cpython-311.pyc +test_jsonschema_test_suite.cpython-311.pyc +test_types.cpython-311.pyc +test_utils.cpython-311.pyc +test_validators.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema/tests/typing: +__init__.py +__pycache__ +test_all_concrete_validators_match_protocol.py +./aylm_env/lib/python3.11/site-packages/jsonschema/tests/typing/__pycache__: +__init__.cpython-311.pyc +test_all_concrete_validators_match_protocol.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema-4.26.0.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/jsonschema-4.26.0.dist-info/licenses: +COPYING +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications: +__init__.py __pycache__ _core.py schemas tests +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/__pycache__: +__init__.cpython-311.pyc _core.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas: +draft201909 draft3 draft6 +draft202012 draft4 draft7 +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft201909: +metaschema.json vocabularies +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies: +applicator core meta-data +content format validation +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft202012: +metaschema.json vocabularies +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies: +applicator format-annotation unevaluated +content format-assertion validation +core meta-data +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft3: +metaschema.json +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft4: +metaschema.json +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft6: +metaschema.json +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/schemas/draft7: +metaschema.json +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/tests: +__init__.py test_jsonschema_specifications.py +__pycache__ +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications/tests/__pycache__: +__init__.cpython-311.pyc +test_jsonschema_specifications.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications-2025.9.1.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses: +COPYING +./aylm_env/lib/python3.11/site-packages/jupyter_core: +__init__.py application.py paths.py utils +__main__.py command.py py.typed version.py +__pycache__ migrate.py troubleshoot.py +./aylm_env/lib/python3.11/site-packages/jupyter_core/__pycache__: +__init__.cpython-311.pyc migrate.cpython-311.pyc +__main__.cpython-311.pyc paths.cpython-311.pyc +application.cpython-311.pyc troubleshoot.cpython-311.pyc +command.cpython-311.pyc version.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jupyter_core/utils: +__init__.py __pycache__ +./aylm_env/lib/python3.11/site-packages/jupyter_core/utils/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/jupyter_core-5.9.1.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/jupyter_core-5.9.1.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/kiwisolver: +__init__.py _cext.pyi +__pycache__ exceptions.py +_cext.cpython-311-darwin.so py.typed +./aylm_env/lib/python3.11/site-packages/kiwisolver/__pycache__: +__init__.cpython-311.pyc exceptions.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/kiwisolver-1.4.9.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/kiwisolver-1.4.9.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/librt: +__init__.pyi strings.cpython-311-darwin.so +base64.cpython-311-darwin.so strings.pyi +base64.pyi time.cpython-311-darwin.so +internal.cpython-311-darwin.so time.pyi +internal.pyi vecs.cpython-311-darwin.so +py.typed vecs.pyi +./aylm_env/lib/python3.11/site-packages/librt-0.8.1.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/librt-0.8.1.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/markdown_it: +__init__.py parser_block.py ruler.py +_compat.py parser_core.py rules_block +_punycode.py parser_inline.py rules_core +cli port.yaml rules_inline +common presets token.py +helpers py.typed tree.py +main.py renderer.py utils.py +./aylm_env/lib/python3.11/site-packages/markdown_it/cli: +__init__.py parse.py +./aylm_env/lib/python3.11/site-packages/markdown_it/common: +__init__.py html_blocks.py normalize_url.py +entities.py html_re.py utils.py +./aylm_env/lib/python3.11/site-packages/markdown_it/helpers: +__init__.py parse_link_label.py +parse_link_destination.py parse_link_title.py +./aylm_env/lib/python3.11/site-packages/markdown_it/presets: +__init__.py commonmark.py default.py zero.py +./aylm_env/lib/python3.11/site-packages/markdown_it/rules_block: +__init__.py fence.py html_block.py paragraph.py table.py +blockquote.py heading.py lheading.py reference.py +code.py hr.py list.py state_block.py +./aylm_env/lib/python3.11/site-packages/markdown_it/rules_core: +__init__.py inline.py normalize.py smartquotes.py text_join.py +block.py linkify.py replacements.py state_core.py +./aylm_env/lib/python3.11/site-packages/markdown_it/rules_inline: +__init__.py escape.py newline.py +autolink.py fragments_join.py state_inline.py +backticks.py html_inline.py strikethrough.py +balance_pairs.py image.py text.py +emphasis.py link.py +entity.py linkify.py +./aylm_env/lib/python3.11/site-packages/markdown_it_py-4.0.0.dist-info: +INSTALLER RECORD entry_points.txt +METADATA WHEEL licenses +./aylm_env/lib/python3.11/site-packages/markdown_it_py-4.0.0.dist-info/licenses: +LICENSE LICENSE.markdown-it +./aylm_env/lib/python3.11/site-packages/markupsafe: +__init__.py _speedups.cpython-311-darwin.so +__pycache__ _speedups.pyi +_native.py py.typed +_speedups.c +./aylm_env/lib/python3.11/site-packages/markupsafe/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/markupsafe-3.0.3.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/markupsafe-3.0.3.dist-info/licenses: +LICENSE.txt +./aylm_env/lib/python3.11/site-packages/matplotlib: +__init__.py figure.py +__init__.pyi figure.pyi +__pycache__ font_manager.py +_afm.py font_manager.pyi +_animation_data.py ft2font.cpython-311-darwin.so +_api ft2font.pyi +_blocking_input.py gridspec.py +_c_internal_utils.cpython-311-darwin.so gridspec.pyi +_c_internal_utils.pyi hatch.py +_cm.py hatch.pyi +_cm_bivar.py image.py +_cm_listed.py image.pyi +_cm_multivar.py inset.py +_color_data.py inset.pyi +_color_data.pyi layout_engine.py +_constrained_layout.py layout_engine.pyi +_docstring.py legend.py +_docstring.pyi legend.pyi +_enums.py legend_handler.py +_enums.pyi legend_handler.pyi +_fontconfig_pattern.py lines.py +_image.cpython-311-darwin.so lines.pyi +_image.pyi markers.py +_internal_utils.py markers.pyi +_layoutgrid.py mathtext.py +_mathtext.py mathtext.pyi +_mathtext_data.py mlab.py +_path.cpython-311-darwin.so mlab.pyi +_path.pyi mpl-data +_pylab_helpers.py offsetbox.py +_pylab_helpers.pyi offsetbox.pyi +_qhull.cpython-311-darwin.so patches.py +_qhull.pyi patches.pyi +_text_helpers.py path.py +_tight_bbox.py path.pyi +_tight_layout.py patheffects.py +_tri.cpython-311-darwin.so patheffects.pyi +_tri.pyi projections +_type1font.py py.typed +_version.py pylab.py +animation.py pyplot.py +animation.pyi quiver.py +artist.py quiver.pyi +artist.pyi rcsetup.py +axes rcsetup.pyi +axis.py sankey.py +axis.pyi sankey.pyi +backend_bases.py scale.py +backend_bases.pyi scale.pyi +backend_managers.py sphinxext +backend_managers.pyi spines.py +backend_tools.py spines.pyi +backend_tools.pyi stackplot.py +backends stackplot.pyi +bezier.py streamplot.py +bezier.pyi streamplot.pyi +category.py style +cbook.py table.py +cbook.pyi table.pyi +cm.py testing +cm.pyi tests +collections.py texmanager.py +collections.pyi texmanager.pyi +colorbar.py text.py +colorbar.pyi text.pyi +colorizer.py textpath.py +colorizer.pyi textpath.pyi +colors.py ticker.py +colors.pyi ticker.pyi +container.py transforms.py +container.pyi transforms.pyi +contour.py tri +contour.pyi typing.py +dates.py units.py +dviread.py widgets.py +dviread.pyi widgets.pyi +./aylm_env/lib/python3.11/site-packages/matplotlib/__pycache__: +__init__.cpython-311.pyc contour.cpython-311.pyc +_afm.cpython-311.pyc dates.cpython-311.pyc +_blocking_input.cpython-311.pyc dviread.cpython-311.pyc +_cm.cpython-311.pyc figure.cpython-311.pyc +_cm_bivar.cpython-311.pyc font_manager.cpython-311.pyc +_cm_listed.cpython-311.pyc gridspec.cpython-311.pyc +_cm_multivar.cpython-311.pyc hatch.cpython-311.pyc +_color_data.cpython-311.pyc image.cpython-311.pyc +_constrained_layout.cpython-311.pyc inset.cpython-311.pyc +_docstring.cpython-311.pyc layout_engine.cpython-311.pyc +_enums.cpython-311.pyc legend.cpython-311.pyc +_fontconfig_pattern.cpython-311.pyc legend_handler.cpython-311.pyc +_layoutgrid.cpython-311.pyc lines.cpython-311.pyc +_mathtext.cpython-311.pyc markers.cpython-311.pyc +_mathtext_data.cpython-311.pyc mathtext.cpython-311.pyc +_pylab_helpers.cpython-311.pyc mlab.cpython-311.pyc +_text_helpers.cpython-311.pyc offsetbox.cpython-311.pyc +_tight_bbox.cpython-311.pyc patches.cpython-311.pyc +_tight_layout.cpython-311.pyc path.cpython-311.pyc +_version.cpython-311.pyc pyplot.cpython-311.pyc +artist.cpython-311.pyc quiver.cpython-311.pyc +axis.cpython-311.pyc rcsetup.cpython-311.pyc +backend_bases.cpython-311.pyc scale.cpython-311.pyc +backend_managers.cpython-311.pyc spines.cpython-311.pyc +backend_tools.cpython-311.pyc stackplot.cpython-311.pyc +bezier.cpython-311.pyc streamplot.cpython-311.pyc +category.cpython-311.pyc table.cpython-311.pyc +cbook.cpython-311.pyc texmanager.cpython-311.pyc +cm.cpython-311.pyc text.cpython-311.pyc +collections.cpython-311.pyc textpath.cpython-311.pyc +colorbar.cpython-311.pyc ticker.cpython-311.pyc +colorizer.cpython-311.pyc transforms.cpython-311.pyc +colors.cpython-311.pyc units.cpython-311.pyc +container.cpython-311.pyc widgets.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/_api: +__init__.py __init__.pyi __pycache__ deprecation.py deprecation.pyi +./aylm_env/lib/python3.11/site-packages/matplotlib/_api/__pycache__: +__init__.cpython-311.pyc deprecation.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/axes: +__init__.py _axes.py _base.pyi +__init__.pyi _axes.pyi _secondary_axes.py +__pycache__ _base.py _secondary_axes.pyi +./aylm_env/lib/python3.11/site-packages/matplotlib/axes/__pycache__: +__init__.cpython-311.pyc _base.cpython-311.pyc +_axes.cpython-311.pyc _secondary_axes.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/backends: +__init__.py backend_pdf.py +__pycache__ backend_pgf.py +_backend_agg.cpython-311-darwin.so backend_ps.py +_backend_agg.pyi backend_qt.py +_backend_gtk.py backend_qt5.py +_backend_pdf_ps.py backend_qt5agg.py +_backend_tk.py backend_qt5cairo.py +_macosx.cpython-311-darwin.so backend_qtagg.py +_macosx.pyi backend_qtcairo.py +_tkagg.cpython-311-darwin.so backend_svg.py +_tkagg.pyi backend_template.py +backend_agg.py backend_tkagg.py +backend_cairo.py backend_tkcairo.py +backend_gtk3.py backend_webagg.py +backend_gtk3agg.py backend_webagg_core.py +backend_gtk3cairo.py backend_wx.py +backend_gtk4.py backend_wxagg.py +backend_gtk4agg.py backend_wxcairo.py +backend_gtk4cairo.py qt_compat.py +backend_macosx.py qt_editor +backend_mixed.py registry.py +backend_nbagg.py web_backend +./aylm_env/lib/python3.11/site-packages/matplotlib/backends/__pycache__: +__init__.cpython-311.pyc registry.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/backends/qt_editor: +__init__.py _formlayout.py figureoptions.py +./aylm_env/lib/python3.11/site-packages/matplotlib/backends/web_backend: +all_figures.html js +css single_figure.html +ipython_inline_figure.html +./aylm_env/lib/python3.11/site-packages/matplotlib/backends/web_backend/css: +boilerplate.css fbm.css mpl.css page.css +./aylm_env/lib/python3.11/site-packages/matplotlib/backends/web_backend/js: +mpl.js mpl_tornado.js nbagg_mpl.js +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data: +fonts kpsewhich.lua plot_directive stylelib +images matplotlibrc sample_data +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/fonts: +afm pdfcorefonts ttf +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/afm: +cmex10.afm pbkdi8a.afm phvbo8an.afm pncri8a.afm putb8a.afm +cmmi10.afm pbkl8a.afm phvl8a.afm pplb8a.afm putbi8a.afm +cmr10.afm pbkli8a.afm phvlo8a.afm pplbi8a.afm putr8a.afm +cmsy10.afm pcrb8a.afm phvr8a.afm pplr8a.afm putri8a.afm +cmtt10.afm pcrbo8a.afm phvr8an.afm pplri8a.afm pzcmi8a.afm +pagd8a.afm pcrr8a.afm phvro8a.afm psyr.afm pzdr.afm +pagdo8a.afm pcrro8a.afm phvro8an.afm ptmb8a.afm +pagk8a.afm phvb8a.afm pncb8a.afm ptmbi8a.afm +pagko8a.afm phvb8an.afm pncbi8a.afm ptmr8a.afm +pbkd8a.afm phvbo8a.afm pncr8a.afm ptmri8a.afm +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts: +Courier-Bold.afm Symbol.afm +Courier-BoldOblique.afm Times-Bold.afm +Courier-Oblique.afm Times-BoldItalic.afm +Courier.afm Times-Italic.afm +Helvetica-Bold.afm Times-Roman.afm +Helvetica-BoldOblique.afm ZapfDingbats.afm +Helvetica-Oblique.afm readme.txt +Helvetica.afm +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/fonts/ttf: +DejaVuSans-Bold.ttf STIXNonUni.ttf +DejaVuSans-BoldOblique.ttf STIXNonUniBol.ttf +DejaVuSans-Oblique.ttf STIXNonUniBolIta.ttf +DejaVuSans.ttf STIXNonUniIta.ttf +DejaVuSansDisplay.ttf STIXSizFiveSymReg.ttf +DejaVuSansMono-Bold.ttf STIXSizFourSymBol.ttf +DejaVuSansMono-BoldOblique.ttf STIXSizFourSymReg.ttf +DejaVuSansMono-Oblique.ttf STIXSizOneSymBol.ttf +DejaVuSansMono.ttf STIXSizOneSymReg.ttf +DejaVuSerif-Bold.ttf STIXSizThreeSymBol.ttf +DejaVuSerif-BoldItalic.ttf STIXSizThreeSymReg.ttf +DejaVuSerif-Italic.ttf STIXSizTwoSymBol.ttf +DejaVuSerif.ttf STIXSizTwoSymReg.ttf +DejaVuSerifDisplay.ttf cmb10.ttf +LICENSE_DEJAVU cmex10.ttf +LICENSE_STIX cmmi10.ttf +STIXGeneral.ttf cmr10.ttf +STIXGeneralBol.ttf cmss10.ttf +STIXGeneralBolIta.ttf cmsy10.ttf +STIXGeneralItalic.ttf cmtt10.ttf +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/images: +back-symbolic.svg home.svg +back.pdf home_large.png +back.png matplotlib.pdf +back.svg matplotlib.png +back_large.png matplotlib.svg +filesave-symbolic.svg matplotlib_large.png +filesave.pdf move-symbolic.svg +filesave.png move.pdf +filesave.svg move.png +filesave_large.png move.svg +forward-symbolic.svg move_large.png +forward.pdf qt4_editor_options.pdf +forward.png qt4_editor_options.png +forward.svg qt4_editor_options.svg +forward_large.png qt4_editor_options_large.png +hand.pdf subplots-symbolic.svg +hand.png subplots.pdf +hand.svg subplots.png +help-symbolic.svg subplots.svg +help.pdf subplots_large.png +help.png zoom_to_rect-symbolic.svg +help.svg zoom_to_rect.pdf +help_large.png zoom_to_rect.png +home-symbolic.svg zoom_to_rect.svg +home.pdf zoom_to_rect_large.png +home.png +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/plot_directive: +plot_directive.css +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/sample_data: +Minduka_Present_Blue_Pack.png grace_hopper.jpg +README.txt jacksboro_fault_dem.npz +Stocks.csv logo2.png +axes_grid membrane.dat +data_x_x2_x3.csv msft.csv +eeg.dat s1045.ima.gz +embedding_in_wx3.xrc topobathy.npz +goog.npz +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/sample_data/axes_grid: +bivariate_normal.npy +./aylm_env/lib/python3.11/site-packages/matplotlib/mpl-data/stylelib: +Solarize_Light2.mplstyle seaborn-v0_8-dark.mplstyle +_classic_test_patch.mplstyle seaborn-v0_8-darkgrid.mplstyle +_mpl-gallery-nogrid.mplstyle seaborn-v0_8-deep.mplstyle +_mpl-gallery.mplstyle seaborn-v0_8-muted.mplstyle +bmh.mplstyle seaborn-v0_8-notebook.mplstyle +classic.mplstyle seaborn-v0_8-paper.mplstyle +dark_background.mplstyle seaborn-v0_8-pastel.mplstyle +fast.mplstyle seaborn-v0_8-poster.mplstyle +fivethirtyeight.mplstyle seaborn-v0_8-talk.mplstyle +ggplot.mplstyle seaborn-v0_8-ticks.mplstyle +grayscale.mplstyle seaborn-v0_8-white.mplstyle +petroff10.mplstyle seaborn-v0_8-whitegrid.mplstyle +seaborn-v0_8-bright.mplstyle seaborn-v0_8.mplstyle +seaborn-v0_8-colorblind.mplstyle tableau-colorblind10.mplstyle +seaborn-v0_8-dark-palette.mplstyle +./aylm_env/lib/python3.11/site-packages/matplotlib/projections: +__init__.py __pycache__ geo.pyi polar.pyi +__init__.pyi geo.py polar.py +./aylm_env/lib/python3.11/site-packages/matplotlib/projections/__pycache__: +__init__.cpython-311.pyc polar.cpython-311.pyc +geo.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/sphinxext: +__init__.py mathmpl.py roles.py +figmpl_directive.py plot_directive.py +./aylm_env/lib/python3.11/site-packages/matplotlib/style: +__init__.py __pycache__ core.py core.pyi +./aylm_env/lib/python3.11/site-packages/matplotlib/style/__pycache__: +__init__.cpython-311.pyc core.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib/testing: +__init__.py compare.py conftest.pyi exceptions.py widgets.pyi +__init__.pyi compare.pyi decorators.py jpl_units +_markers.py conftest.py decorators.pyi widgets.py +./aylm_env/lib/python3.11/site-packages/matplotlib/testing/jpl_units: +Duration.py StrConverter.py UnitDblFormatter.py +Epoch.py UnitDbl.py __init__.py +EpochConverter.py UnitDblConverter.py +./aylm_env/lib/python3.11/site-packages/matplotlib/tests: +__init__.py test_font_manager.py +conftest.py test_fontconfig_pattern.py +test_afm.py test_ft2font.py +test_agg.py test_getattr.py +test_agg_filter.py test_gridspec.py +test_animation.py test_image.py +test_api.py test_legend.py +test_arrow_patches.py test_lines.py +test_artist.py test_marker.py +test_axes.py test_mathtext.py +test_axis.py test_matplotlib.py +test_backend_bases.py test_mlab.py +test_backend_cairo.py test_multivariate_colormaps.py +test_backend_gtk3.py test_offsetbox.py +test_backend_inline.py test_patches.py +test_backend_macosx.py test_path.py +test_backend_nbagg.py test_patheffects.py +test_backend_pdf.py test_pickle.py +test_backend_pgf.py test_png.py +test_backend_ps.py test_polar.py +test_backend_qt.py test_preprocess_data.py +test_backend_registry.py test_pyplot.py +test_backend_svg.py test_quiver.py +test_backend_template.py test_rcparams.py +test_backend_tk.py test_sankey.py +test_backend_tools.py test_scale.py +test_backend_webagg.py test_simplification.py +test_backends_interactive.py test_skew.py +test_basic.py test_sphinxext.py +test_bbox_tight.py test_spines.py +test_bezier.py test_streamplot.py +test_category.py test_style.py +test_cbook.py test_subplots.py +test_collections.py test_table.py +test_colorbar.py test_testing.py +test_colors.py test_texmanager.py +test_compare_images.py test_text.py +test_constrainedlayout.py test_textpath.py +test_container.py test_ticker.py +test_contour.py test_tightlayout.py +test_cycles.py test_transforms.py +test_dates.py test_triangulation.py +test_datetime.py test_type1font.py +test_determinism.py test_units.py +test_doc.py test_usetex.py +test_dviread.py test_widgets.py +test_figure.py +./aylm_env/lib/python3.11/site-packages/matplotlib/tri: +__init__.py _trifinder.py _triplot.py +__pycache__ _trifinder.pyi _triplot.pyi +_triangulation.py _triinterpolate.py _trirefine.py +_triangulation.pyi _triinterpolate.pyi _trirefine.pyi +_tricontour.py _tripcolor.py _tritools.py +_tricontour.pyi _tripcolor.pyi _tritools.pyi +./aylm_env/lib/python3.11/site-packages/matplotlib/tri/__pycache__: +__init__.cpython-311.pyc _tripcolor.cpython-311.pyc +_triangulation.cpython-311.pyc _triplot.cpython-311.pyc +_tricontour.cpython-311.pyc _trirefine.cpython-311.pyc +_trifinder.cpython-311.pyc _tritools.cpython-311.pyc +_triinterpolate.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/matplotlib-3.10.8.dist-info: +INSTALLER METADATA REQUESTED +LICENSE RECORD WHEEL +./aylm_env/lib/python3.11/site-packages/mdurl: +__init__.py _encode.py _parse.py py.typed +_decode.py _format.py _url.py +./aylm_env/lib/python3.11/site-packages/mdurl-0.1.2.dist-info: +INSTALLER LICENSE METADATA RECORD WHEEL +./aylm_env/lib/python3.11/site-packages/ml_dtypes: +__init__.py _iinfo.py +__pycache__ _ml_dtypes_ext.cpython-311-darwin.so +_finfo.py py.typed +./aylm_env/lib/python3.11/site-packages/ml_dtypes/__pycache__: +__init__.cpython-311.pyc _iinfo.cpython-311.pyc +_finfo.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/ml_dtypes-0.5.4.dist-info: +INSTALLER RECORD licenses +METADATA WHEEL top_level.txt +./aylm_env/lib/python3.11/site-packages/ml_dtypes-0.5.4.dist-info/licenses: +LICENSE LICENSE.eigen +./aylm_env/lib/python3.11/site-packages/mpl_toolkits: +axes_grid1 axisartist mplot3d +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/axes_grid1: +__init__.py axes_rgb.py parasite_axes.py +anchored_artists.py axes_size.py tests +axes_divider.py inset_locator.py +axes_grid.py mpl_axes.py +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/axes_grid1/tests: +__init__.py conftest.py test_axes_grid1.py +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/axisartist: +__init__.py floating_axes.py +angle_helper.py grid_finder.py +axes_divider.py grid_helper_curvelinear.py +axis_artist.py parasite_axes.py +axisline_style.py tests +axislines.py +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/axisartist/tests: +__init__.py test_axislines.py +conftest.py test_floating_axes.py +test_angle_helper.py test_grid_finder.py +test_axis_artist.py test_grid_helper_curvelinear.py +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/mplot3d: +__init__.py art3d.py axis3d.py tests +__pycache__ axes3d.py proj3d.py +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/mplot3d/__pycache__: +__init__.cpython-311.pyc axis3d.cpython-311.pyc +art3d.cpython-311.pyc proj3d.cpython-311.pyc +axes3d.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpl_toolkits/mplot3d/tests: +__init__.py test_art3d.py test_legend3d.py +conftest.py test_axes3d.py +./aylm_env/lib/python3.11/site-packages/mpmath: +__init__.py ctx_mp.py math2.py +__pycache__ ctx_mp_python.py matrices +calculus function_docs.py rational.py +ctx_base.py functions tests +ctx_fp.py identification.py usertools.py +ctx_iv.py libmp visualization.py +./aylm_env/lib/python3.11/site-packages/mpmath/__pycache__: +__init__.cpython-311.pyc function_docs.cpython-311.pyc +ctx_base.cpython-311.pyc identification.cpython-311.pyc +ctx_fp.cpython-311.pyc math2.cpython-311.pyc +ctx_iv.cpython-311.pyc rational.cpython-311.pyc +ctx_mp.cpython-311.pyc usertools.cpython-311.pyc +ctx_mp_python.cpython-311.pyc visualization.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpmath/calculus: +__init__.py differentiation.py optimization.py +__pycache__ extrapolation.py polynomials.py +approximation.py inverselaplace.py quadrature.py +calculus.py odes.py +./aylm_env/lib/python3.11/site-packages/mpmath/calculus/__pycache__: +__init__.cpython-311.pyc inverselaplace.cpython-311.pyc +approximation.cpython-311.pyc odes.cpython-311.pyc +calculus.cpython-311.pyc optimization.cpython-311.pyc +differentiation.cpython-311.pyc polynomials.cpython-311.pyc +extrapolation.cpython-311.pyc quadrature.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpmath/functions: +__init__.py factorials.py rszeta.py +__pycache__ functions.py signals.py +bessel.py hypergeometric.py theta.py +elliptic.py orthogonal.py zeta.py +expintegrals.py qfunctions.py zetazeros.py +./aylm_env/lib/python3.11/site-packages/mpmath/functions/__pycache__: +__init__.cpython-311.pyc orthogonal.cpython-311.pyc +bessel.cpython-311.pyc qfunctions.cpython-311.pyc +elliptic.cpython-311.pyc rszeta.cpython-311.pyc +expintegrals.cpython-311.pyc signals.cpython-311.pyc +factorials.cpython-311.pyc theta.cpython-311.pyc +functions.cpython-311.pyc zeta.cpython-311.pyc +hypergeometric.cpython-311.pyc zetazeros.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpmath/libmp: +__init__.py backend.py libelefun.py libintmath.py libmpf.py +__pycache__ gammazeta.py libhyper.py libmpc.py libmpi.py +./aylm_env/lib/python3.11/site-packages/mpmath/libmp/__pycache__: +__init__.cpython-311.pyc libintmath.cpython-311.pyc +backend.cpython-311.pyc libmpc.cpython-311.pyc +gammazeta.cpython-311.pyc libmpf.cpython-311.pyc +libelefun.cpython-311.pyc libmpi.cpython-311.pyc +libhyper.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpmath/matrices: +__init__.py eigen.py matrices.py +__pycache__ eigen_symmetric.py +calculus.py linalg.py +./aylm_env/lib/python3.11/site-packages/mpmath/matrices/__pycache__: +__init__.cpython-311.pyc eigen_symmetric.cpython-311.pyc +calculus.cpython-311.pyc linalg.cpython-311.pyc +eigen.cpython-311.pyc matrices.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mpmath/tests: +__init__.py test_eigen_symmetric.py test_mpmath.py +extratest_gamma.py test_elliptic.py test_ode.py +extratest_zeta.py test_fp.py test_pickle.py +runtests.py test_functions.py test_power.py +test_basic_ops.py test_functions2.py test_quad.py +test_bitwise.py test_gammazeta.py test_rootfinding.py +test_calculus.py test_hp.py test_special.py +test_compatibility.py test_identify.py test_str.py +test_convert.py test_interval.py test_summation.py +test_diff.py test_levin.py test_trig.py +test_division.py test_linalg.py test_visualization.py +test_eigen.py test_matrices.py torture.py +./aylm_env/lib/python3.11/site-packages/mpmath-1.3.0.dist-info: +INSTALLER METADATA WHEEL +LICENSE RECORD top_level.txt +./aylm_env/lib/python3.11/site-packages/mypy: +__init__.cpython-311-darwin.so +__init__.py +__main__.py +__pycache__ +api.cpython-311-darwin.so +api.py +applytype.cpython-311-darwin.so +applytype.py +argmap.cpython-311-darwin.so +argmap.py +binder.cpython-311-darwin.so +binder.py +bogus_type.py +build.cpython-311-darwin.so +build.py +cache.cpython-311-darwin.so +cache.py +checker.cpython-311-darwin.so +checker.py +checker_shared.cpython-311-darwin.so +checker_shared.py +checker_state.cpython-311-darwin.so +checker_state.py +checkexpr.cpython-311-darwin.so +checkexpr.py +checkmember.cpython-311-darwin.so +checkmember.py +checkpattern.cpython-311-darwin.so +checkpattern.py +checkstrformat.cpython-311-darwin.so +checkstrformat.py +config_parser.cpython-311-darwin.so +config_parser.py +constant_fold.cpython-311-darwin.so +constant_fold.py +constraints.cpython-311-darwin.so +constraints.py +copytype.cpython-311-darwin.so +copytype.py +defaults.cpython-311-darwin.so +defaults.py +dmypy +dmypy_os.cpython-311-darwin.so +dmypy_os.py +dmypy_server.cpython-311-darwin.so +dmypy_server.py +dmypy_util.cpython-311-darwin.so +dmypy_util.py +erasetype.cpython-311-darwin.so +erasetype.py +error_formatter.cpython-311-darwin.so +error_formatter.py +errorcodes.cpython-311-darwin.so +errorcodes.py +errors.cpython-311-darwin.so +errors.py +evalexpr.cpython-311-darwin.so +evalexpr.py +expandtype.cpython-311-darwin.so +expandtype.py +exportjson.py +exprtotype.cpython-311-darwin.so +exprtotype.py +fastparse.cpython-311-darwin.so +fastparse.py +find_sources.cpython-311-darwin.so +find_sources.py +fixup.cpython-311-darwin.so +fixup.py +freetree.cpython-311-darwin.so +freetree.py +fscache.cpython-311-darwin.so +fscache.py +fswatcher.cpython-311-darwin.so +fswatcher.py +gclogger.cpython-311-darwin.so +gclogger.py +git.cpython-311-darwin.so +git.py +graph_utils.cpython-311-darwin.so +graph_utils.py +indirection.cpython-311-darwin.so +indirection.py +infer.cpython-311-darwin.so +infer.py +inspections.cpython-311-darwin.so +inspections.py +ipc.cpython-311-darwin.so +ipc.py +join.cpython-311-darwin.so +join.py +literals.cpython-311-darwin.so +literals.py +lookup.cpython-311-darwin.so +lookup.py +main.cpython-311-darwin.so +main.py +maptype.cpython-311-darwin.so +maptype.py +meet.cpython-311-darwin.so +meet.py +memprofile.cpython-311-darwin.so +memprofile.py +message_registry.cpython-311-darwin.so +message_registry.py +messages.cpython-311-darwin.so +messages.py +metastore.cpython-311-darwin.so +metastore.py +mixedtraverser.cpython-311-darwin.so +mixedtraverser.py +modulefinder.cpython-311-darwin.so +modulefinder.py +moduleinspect.cpython-311-darwin.so +moduleinspect.py +mro.cpython-311-darwin.so +mro.py +nodes.cpython-311-darwin.so +nodes.py +operators.cpython-311-darwin.so +operators.py +options.cpython-311-darwin.so +options.py +parse.cpython-311-darwin.so +parse.py +partially_defined.cpython-311-darwin.so +partially_defined.py +patterns.cpython-311-darwin.so +patterns.py +plugin.cpython-311-darwin.so +plugin.py +plugins +py.typed +pyinfo.py +reachability.cpython-311-darwin.so +reachability.py +refinfo.cpython-311-darwin.so +refinfo.py +renaming.cpython-311-darwin.so +renaming.py +report.cpython-311-darwin.so +report.py +scope.cpython-311-darwin.so +scope.py +semanal.cpython-311-darwin.so +semanal.py +semanal_classprop.cpython-311-darwin.so +semanal_classprop.py +semanal_enum.cpython-311-darwin.so +semanal_enum.py +semanal_infer.cpython-311-darwin.so +semanal_infer.py +semanal_main.cpython-311-darwin.so +semanal_main.py +semanal_namedtuple.cpython-311-darwin.so +semanal_namedtuple.py +semanal_newtype.cpython-311-darwin.so +semanal_newtype.py +semanal_pass1.cpython-311-darwin.so +semanal_pass1.py +semanal_shared.cpython-311-darwin.so +semanal_shared.py +semanal_typeargs.cpython-311-darwin.so +semanal_typeargs.py +semanal_typeddict.cpython-311-darwin.so +semanal_typeddict.py +server +sharedparse.cpython-311-darwin.so +sharedparse.py +solve.cpython-311-darwin.so +solve.py +split_namespace.py +state.cpython-311-darwin.so +state.py +stats.cpython-311-darwin.so +stats.py +strconv.cpython-311-darwin.so +strconv.py +stubdoc.py +stubgen.cpython-311-darwin.so +stubgen.py +stubgenc.py +stubinfo.cpython-311-darwin.so +stubinfo.py +stubtest.py +stubutil.cpython-311-darwin.so +stubutil.py +subtypes.cpython-311-darwin.so +subtypes.py +suggestions.cpython-311-darwin.so +suggestions.py +test +traverser.cpython-311-darwin.so +traverser.py +treetransform.cpython-311-darwin.so +treetransform.py +tvar_scope.cpython-311-darwin.so +tvar_scope.py +type_visitor.cpython-311-darwin.so +type_visitor.py +typeanal.cpython-311-darwin.so +typeanal.py +typeops.cpython-311-darwin.so +typeops.py +types.cpython-311-darwin.so +types.py +types_utils.cpython-311-darwin.so +types_utils.py +typeshed +typestate.cpython-311-darwin.so +typestate.py +typetraverser.cpython-311-darwin.so +typetraverser.py +typevars.cpython-311-darwin.so +typevars.py +typevartuples.cpython-311-darwin.so +typevartuples.py +util.cpython-311-darwin.so +util.py +version.py +visitor.cpython-311-darwin.so +visitor.py +xml +./aylm_env/lib/python3.11/site-packages/mypy/__pycache__: +__init__.cpython-311.pyc modulefinder.cpython-311.pyc +__main__.cpython-311.pyc moduleinspect.cpython-311.pyc +api.cpython-311.pyc mro.cpython-311.pyc +applytype.cpython-311.pyc nodes.cpython-311.pyc +argmap.cpython-311.pyc operators.cpython-311.pyc +binder.cpython-311.pyc options.cpython-311.pyc +bogus_type.cpython-311.pyc parse.cpython-311.pyc +build.cpython-311.pyc partially_defined.cpython-311.pyc +cache.cpython-311.pyc patterns.cpython-311.pyc +checker.cpython-311.pyc plugin.cpython-311.pyc +checker_shared.cpython-311.pyc pyinfo.cpython-311.pyc +checker_state.cpython-311.pyc reachability.cpython-311.pyc +checkexpr.cpython-311.pyc refinfo.cpython-311.pyc +checkmember.cpython-311.pyc renaming.cpython-311.pyc +checkpattern.cpython-311.pyc report.cpython-311.pyc +checkstrformat.cpython-311.pyc scope.cpython-311.pyc +config_parser.cpython-311.pyc semanal.cpython-311.pyc +constant_fold.cpython-311.pyc semanal_classprop.cpython-311.pyc +constraints.cpython-311.pyc semanal_enum.cpython-311.pyc +copytype.cpython-311.pyc semanal_infer.cpython-311.pyc +defaults.cpython-311.pyc semanal_main.cpython-311.pyc +dmypy_os.cpython-311.pyc semanal_namedtuple.cpython-311.pyc +dmypy_server.cpython-311.pyc semanal_newtype.cpython-311.pyc +dmypy_util.cpython-311.pyc semanal_pass1.cpython-311.pyc +erasetype.cpython-311.pyc semanal_shared.cpython-311.pyc +error_formatter.cpython-311.pyc semanal_typeargs.cpython-311.pyc +errorcodes.cpython-311.pyc semanal_typeddict.cpython-311.pyc +errors.cpython-311.pyc sharedparse.cpython-311.pyc +evalexpr.cpython-311.pyc solve.cpython-311.pyc +expandtype.cpython-311.pyc split_namespace.cpython-311.pyc +exportjson.cpython-311.pyc state.cpython-311.pyc +exprtotype.cpython-311.pyc stats.cpython-311.pyc +fastparse.cpython-311.pyc strconv.cpython-311.pyc +find_sources.cpython-311.pyc stubdoc.cpython-311.pyc +fixup.cpython-311.pyc stubgen.cpython-311.pyc +freetree.cpython-311.pyc stubgenc.cpython-311.pyc +fscache.cpython-311.pyc stubinfo.cpython-311.pyc +fswatcher.cpython-311.pyc stubtest.cpython-311.pyc +gclogger.cpython-311.pyc stubutil.cpython-311.pyc +git.cpython-311.pyc subtypes.cpython-311.pyc +graph_utils.cpython-311.pyc suggestions.cpython-311.pyc +indirection.cpython-311.pyc traverser.cpython-311.pyc +infer.cpython-311.pyc treetransform.cpython-311.pyc +inspections.cpython-311.pyc tvar_scope.cpython-311.pyc +ipc.cpython-311.pyc type_visitor.cpython-311.pyc +join.cpython-311.pyc typeanal.cpython-311.pyc +literals.cpython-311.pyc typeops.cpython-311.pyc +lookup.cpython-311.pyc types.cpython-311.pyc +main.cpython-311.pyc types_utils.cpython-311.pyc +maptype.cpython-311.pyc typestate.cpython-311.pyc +meet.cpython-311.pyc typetraverser.cpython-311.pyc +memprofile.cpython-311.pyc typevars.cpython-311.pyc +message_registry.cpython-311.pyc typevartuples.cpython-311.pyc +messages.cpython-311.pyc util.cpython-311.pyc +metastore.cpython-311.pyc version.cpython-311.pyc +mixedtraverser.cpython-311.pyc visitor.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/dmypy: +__init__.cpython-311-darwin.so __pycache__ +__init__.py client.cpython-311-darwin.so +__main__.py client.py +./aylm_env/lib/python3.11/site-packages/mypy/dmypy/__pycache__: +__init__.cpython-311.pyc client.cpython-311.pyc +__main__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/plugins: +__init__.cpython-311-darwin.so dataclasses.py +__init__.py default.cpython-311-darwin.so +__pycache__ default.py +attrs.cpython-311-darwin.so enums.cpython-311-darwin.so +attrs.py enums.py +common.cpython-311-darwin.so functools.cpython-311-darwin.so +common.py functools.py +constants.cpython-311-darwin.so proper_plugin.cpython-311-darwin.so +constants.py proper_plugin.py +ctypes.cpython-311-darwin.so singledispatch.cpython-311-darwin.so +ctypes.py singledispatch.py +dataclasses.cpython-311-darwin.so +./aylm_env/lib/python3.11/site-packages/mypy/plugins/__pycache__: +__init__.cpython-311.pyc default.cpython-311.pyc +attrs.cpython-311.pyc enums.cpython-311.pyc +common.cpython-311.pyc functools.cpython-311.pyc +constants.cpython-311.pyc proper_plugin.cpython-311.pyc +ctypes.cpython-311.pyc singledispatch.cpython-311.pyc +dataclasses.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/server: +__init__.cpython-311-darwin.so mergecheck.py +__init__.py objgraph.cpython-311-darwin.so +__pycache__ objgraph.py +astdiff.cpython-311-darwin.so subexpr.cpython-311-darwin.so +astdiff.py subexpr.py +astmerge.cpython-311-darwin.so target.cpython-311-darwin.so +astmerge.py target.py +aststrip.cpython-311-darwin.so trigger.cpython-311-darwin.so +aststrip.py trigger.py +deps.cpython-311-darwin.so update.cpython-311-darwin.so +deps.py update.py +mergecheck.cpython-311-darwin.so +./aylm_env/lib/python3.11/site-packages/mypy/server/__pycache__: +__init__.cpython-311.pyc objgraph.cpython-311.pyc +astdiff.cpython-311.pyc subexpr.cpython-311.pyc +astmerge.cpython-311.pyc target.cpython-311.pyc +aststrip.cpython-311.pyc trigger.cpython-311.pyc +deps.cpython-311.pyc update.cpython-311.pyc +mergecheck.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/test: +__init__.py testinfer.py +__pycache__ testipc.py +config.py testmerge.py +data.py testmodulefinder.py +helpers.py testmypyc.py +meta testoutput.py +test_config_parser.py testparse.py +test_find_sources.py testpep561.py +test_ref_info.py testpythoneval.py +testapi.py testreports.py +testargs.py testsemanal.py +testcheck.py testsolve.py +testcmdline.py teststubgen.py +testconstraints.py teststubinfo.py +testdaemon.py teststubtest.py +testdeps.py testsubtypes.py +testdiff.py testtransform.py +testerrorstream.py testtypegen.py +testexportjson.py testtypes.py +testfinegrained.py testutil.py +testfinegrainedcache.py typefixture.py +testformatter.py update_data.py +testfscache.py visitors.cpython-311-darwin.so +testgraph.py visitors.py +./aylm_env/lib/python3.11/site-packages/mypy/test/__pycache__: +__init__.cpython-311.pyc testipc.cpython-311.pyc +config.cpython-311.pyc testmerge.cpython-311.pyc +data.cpython-311.pyc testmodulefinder.cpython-311.pyc +helpers.cpython-311.pyc testmypyc.cpython-311.pyc +test_config_parser.cpython-311.pyc testoutput.cpython-311.pyc +test_find_sources.cpython-311.pyc testparse.cpython-311.pyc +test_ref_info.cpython-311.pyc testpep561.cpython-311.pyc +testapi.cpython-311.pyc testpythoneval.cpython-311.pyc +testargs.cpython-311.pyc testreports.cpython-311.pyc +testcheck.cpython-311.pyc testsemanal.cpython-311.pyc +testcmdline.cpython-311.pyc testsolve.cpython-311.pyc +testconstraints.cpython-311.pyc teststubgen.cpython-311.pyc +testdaemon.cpython-311.pyc teststubinfo.cpython-311.pyc +testdeps.cpython-311.pyc teststubtest.cpython-311.pyc +testdiff.cpython-311.pyc testsubtypes.cpython-311.pyc +testerrorstream.cpython-311.pyc testtransform.cpython-311.pyc +testexportjson.cpython-311.pyc testtypegen.cpython-311.pyc +testfinegrained.cpython-311.pyc testtypes.cpython-311.pyc +testfinegrainedcache.cpython-311.pyc testutil.cpython-311.pyc +testformatter.cpython-311.pyc typefixture.cpython-311.pyc +testfscache.cpython-311.pyc update_data.cpython-311.pyc +testgraph.cpython-311.pyc visitors.cpython-311.pyc +testinfer.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/test/meta: +__init__.py _pytest.py test_parse_data.py +__pycache__ test_diff_helper.py test_update_data.py +./aylm_env/lib/python3.11/site-packages/mypy/test/meta/__pycache__: +__init__.cpython-311.pyc test_parse_data.cpython-311.pyc +_pytest.cpython-311.pyc test_update_data.cpython-311.pyc +test_diff_helper.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypy/typeshed: +LICENSE stdlib stubs +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib: +VERSIONS imp.pyi +__future__.pyi importlib +__main__.pyi inspect.pyi +_ast.pyi io.pyi +_asyncio.pyi ipaddress.pyi +_bisect.pyi itertools.pyi +_blake2.pyi json +_bootlocale.pyi keyword.pyi +_bz2.pyi lib2to3 +_codecs.pyi linecache.pyi +_collections_abc.pyi locale.pyi +_compat_pickle.pyi logging +_compression.pyi lzma.pyi +_contextvars.pyi mailbox.pyi +_csv.pyi mailcap.pyi +_ctypes.pyi marshal.pyi +_curses.pyi math.pyi +_curses_panel.pyi mimetypes.pyi +_dbm.pyi mmap.pyi +_decimal.pyi modulefinder.pyi +_frozen_importlib.pyi msilib +_frozen_importlib_external.pyi msvcrt.pyi +_gdbm.pyi multiprocessing +_hashlib.pyi netrc.pyi +_heapq.pyi nis.pyi +_imp.pyi nntplib.pyi +_interpchannels.pyi nt.pyi +_interpqueues.pyi ntpath.pyi +_interpreters.pyi nturl2path.pyi +_io.pyi numbers.pyi +_json.pyi opcode.pyi +_locale.pyi operator.pyi +_lsprof.pyi optparse.pyi +_lzma.pyi os +_markupbase.pyi ossaudiodev.pyi +_msi.pyi parser.pyi +_multibytecodec.pyi pathlib +_operator.pyi pdb.pyi +_osx_support.pyi pickle.pyi +_pickle.pyi pickletools.pyi +_posixsubprocess.pyi pipes.pyi +_py_abc.pyi pkgutil.pyi +_pydecimal.pyi platform.pyi +_queue.pyi plistlib.pyi +_random.pyi poplib.pyi +_sitebuiltins.pyi posix.pyi +_socket.pyi posixpath.pyi +_sqlite3.pyi pprint.pyi +_ssl.pyi profile.pyi +_stat.pyi pstats.pyi +_struct.pyi pty.pyi +_thread.pyi pwd.pyi +_threading_local.pyi py_compile.pyi +_tkinter.pyi pyclbr.pyi +_tracemalloc.pyi pydoc.pyi +_typeshed pydoc_data +_warnings.pyi pyexpat +_weakref.pyi queue.pyi +_weakrefset.pyi quopri.pyi +_winapi.pyi random.pyi +_zstd.pyi re.pyi +abc.pyi readline.pyi +aifc.pyi reprlib.pyi +annotationlib.pyi resource.pyi +antigravity.pyi rlcompleter.pyi +argparse.pyi runpy.pyi +array.pyi sched.pyi +ast.pyi secrets.pyi +asynchat.pyi select.pyi +asyncio selectors.pyi +asyncore.pyi shelve.pyi +atexit.pyi shlex.pyi +audioop.pyi shutil.pyi +base64.pyi signal.pyi +bdb.pyi site.pyi +binascii.pyi smtpd.pyi +binhex.pyi smtplib.pyi +bisect.pyi sndhdr.pyi +builtins.pyi socket.pyi +bz2.pyi socketserver.pyi +cProfile.pyi spwd.pyi +calendar.pyi sqlite3 +cgi.pyi sre_compile.pyi +cgitb.pyi sre_constants.pyi +chunk.pyi sre_parse.pyi +cmath.pyi ssl.pyi +cmd.pyi stat.pyi +code.pyi statistics.pyi +codecs.pyi string +codeop.pyi stringprep.pyi +collections struct.pyi +colorsys.pyi subprocess.pyi +compileall.pyi sunau.pyi +compression symbol.pyi +concurrent symtable.pyi +configparser.pyi sys +contextlib.pyi sysconfig.pyi +contextvars.pyi syslog.pyi +copy.pyi tabnanny.pyi +copyreg.pyi tarfile.pyi +crypt.pyi telnetlib.pyi +csv.pyi tempfile.pyi +ctypes termios.pyi +curses textwrap.pyi +dataclasses.pyi this.pyi +datetime.pyi threading.pyi +dbm time.pyi +decimal.pyi timeit.pyi +difflib.pyi tkinter +dis.pyi token.pyi +distutils tokenize.pyi +doctest.pyi tomllib.pyi +email trace.pyi +encodings traceback.pyi +ensurepip tracemalloc.pyi +enum.pyi tty.pyi +errno.pyi turtle.pyi +faulthandler.pyi types.pyi +fcntl.pyi typing.pyi +filecmp.pyi typing_extensions.pyi +fileinput.pyi unicodedata.pyi +fnmatch.pyi unittest +formatter.pyi urllib +fractions.pyi uu.pyi +ftplib.pyi uuid.pyi +functools.pyi venv +gc.pyi warnings.pyi +genericpath.pyi wave.pyi +getopt.pyi weakref.pyi +getpass.pyi webbrowser.pyi +gettext.pyi winreg.pyi +glob.pyi winsound.pyi +graphlib.pyi wsgiref +grp.pyi xdrlib.pyi +gzip.pyi xml +hashlib.pyi xmlrpc +heapq.pyi xxlimited.pyi +hmac.pyi zipapp.pyi +html zipfile +http zipimport.pyi +imaplib.pyi zlib.pyi +imghdr.pyi zoneinfo +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/_typeshed: +__init__.pyi importlib.pyi +_type_checker_internals.pyi wsgi.pyi +dbapi.pyi xml.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/asyncio: +__init__.pyi locks.pyi taskgroups.pyi +base_events.pyi log.pyi tasks.pyi +base_futures.pyi mixins.pyi threads.pyi +base_subprocess.pyi proactor_events.pyi timeouts.pyi +base_tasks.pyi protocols.pyi tools.pyi +constants.pyi queues.pyi transports.pyi +coroutines.pyi runners.pyi trsock.pyi +events.pyi selector_events.pyi unix_events.pyi +exceptions.pyi sslproto.pyi windows_events.pyi +format_helpers.pyi staggered.pyi windows_utils.pyi +futures.pyi streams.pyi +graph.pyi subprocess.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/collections: +__init__.pyi abc.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/compression: +__init__.pyi bz2.pyi lzma.pyi zstd +_common gzip.pyi zlib.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/compression/_common: +__init__.pyi _streams.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/compression/zstd: +__init__.pyi _zstdfile.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/concurrent: +__init__.pyi futures interpreters +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/concurrent/futures: +__init__.pyi _base.pyi interpreter.pyi process.pyi thread.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/concurrent/interpreters: +__init__.pyi _crossinterp.pyi _queues.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/ctypes: +__init__.pyi _endian.pyi macholib util.pyi wintypes.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/ctypes/macholib: +__init__.pyi dyld.pyi dylib.pyi framework.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/curses: +__init__.pyi ascii.pyi has_key.pyi panel.pyi textpad.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/dbm: +__init__.pyi dumb.pyi gnu.pyi ndbm.pyi sqlite3.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/distutils: +__init__.pyi cygwinccompiler.pyi filelist.pyi +_msvccompiler.pyi debug.pyi log.pyi +archive_util.pyi dep_util.pyi msvccompiler.pyi +bcppcompiler.pyi dir_util.pyi spawn.pyi +ccompiler.pyi dist.pyi sysconfig.pyi +cmd.pyi errors.pyi text_file.pyi +command extension.pyi unixccompiler.pyi +config.pyi fancy_getopt.pyi util.pyi +core.pyi file_util.pyi version.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/distutils/command: +__init__.pyi build_clib.pyi install_data.pyi +bdist.pyi build_ext.pyi install_egg_info.pyi +bdist_dumb.pyi build_py.pyi install_headers.pyi +bdist_msi.pyi build_scripts.pyi install_lib.pyi +bdist_packager.pyi check.pyi install_scripts.pyi +bdist_rpm.pyi clean.pyi register.pyi +bdist_wininst.pyi config.pyi sdist.pyi +build.pyi install.pyi upload.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/email: +__init__.pyi header.pyi +_header_value_parser.pyi headerregistry.pyi +_policybase.pyi iterators.pyi +base64mime.pyi message.pyi +charset.pyi mime +contentmanager.pyi parser.pyi +encoders.pyi policy.pyi +errors.pyi quoprimime.pyi +feedparser.pyi utils.pyi +generator.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/email/mime: +__init__.pyi base.pyi multipart.pyi +application.pyi image.pyi nonmultipart.pyi +audio.pyi message.pyi text.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/encodings: +__init__.pyi cp866.pyi koi8_r.pyi +aliases.pyi cp869.pyi koi8_t.pyi +ascii.pyi cp874.pyi koi8_u.pyi +base64_codec.pyi cp875.pyi kz1048.pyi +big5.pyi cp932.pyi latin_1.pyi +big5hkscs.pyi cp949.pyi mac_arabic.pyi +bz2_codec.pyi cp950.pyi mac_croatian.pyi +charmap.pyi euc_jis_2004.pyi mac_cyrillic.pyi +cp037.pyi euc_jisx0213.pyi mac_farsi.pyi +cp1006.pyi euc_jp.pyi mac_greek.pyi +cp1026.pyi euc_kr.pyi mac_iceland.pyi +cp1125.pyi gb18030.pyi mac_latin2.pyi +cp1140.pyi gb2312.pyi mac_roman.pyi +cp1250.pyi gbk.pyi mac_romanian.pyi +cp1251.pyi hex_codec.pyi mac_turkish.pyi +cp1252.pyi hp_roman8.pyi mbcs.pyi +cp1253.pyi hz.pyi oem.pyi +cp1254.pyi idna.pyi palmos.pyi +cp1255.pyi iso2022_jp.pyi ptcp154.pyi +cp1256.pyi iso2022_jp_1.pyi punycode.pyi +cp1257.pyi iso2022_jp_2.pyi quopri_codec.pyi +cp1258.pyi iso2022_jp_2004.pyi raw_unicode_escape.pyi +cp273.pyi iso2022_jp_3.pyi rot_13.pyi +cp424.pyi iso2022_jp_ext.pyi shift_jis.pyi +cp437.pyi iso2022_kr.pyi shift_jis_2004.pyi +cp500.pyi iso8859_1.pyi shift_jisx0213.pyi +cp720.pyi iso8859_10.pyi tis_620.pyi +cp737.pyi iso8859_11.pyi undefined.pyi +cp775.pyi iso8859_13.pyi unicode_escape.pyi +cp850.pyi iso8859_14.pyi utf_16.pyi +cp852.pyi iso8859_15.pyi utf_16_be.pyi +cp855.pyi iso8859_16.pyi utf_16_le.pyi +cp856.pyi iso8859_2.pyi utf_32.pyi +cp857.pyi iso8859_3.pyi utf_32_be.pyi +cp858.pyi iso8859_4.pyi utf_32_le.pyi +cp860.pyi iso8859_5.pyi utf_7.pyi +cp861.pyi iso8859_6.pyi utf_8.pyi +cp862.pyi iso8859_7.pyi utf_8_sig.pyi +cp863.pyi iso8859_8.pyi uu_codec.pyi +cp864.pyi iso8859_9.pyi zlib_codec.pyi +cp865.pyi johab.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/ensurepip: +__init__.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/html: +__init__.pyi entities.pyi parser.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/http: +__init__.pyi client.pyi cookiejar.pyi cookies.pyi server.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/importlib: +__init__.pyi abc.pyi resources +_abc.pyi machinery.pyi simple.pyi +_bootstrap.pyi metadata util.pyi +_bootstrap_external.pyi readers.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/importlib/metadata: +__init__.pyi _meta.pyi diagnose.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/importlib/resources: +__init__.pyi _functional.pyi readers.pyi +_common.pyi abc.pyi simple.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/json: +__init__.pyi decoder.pyi encoder.pyi scanner.pyi tool.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/lib2to3: +__init__.pyi fixer_base.pyi main.pyi pygram.pyi refactor.pyi +btm_matcher.pyi fixes pgen2 pytree.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/lib2to3/fixes: +__init__.pyi fix_methodattrs.pyi +fix_apply.pyi fix_ne.pyi +fix_asserts.pyi fix_next.pyi +fix_basestring.pyi fix_nonzero.pyi +fix_buffer.pyi fix_numliterals.pyi +fix_dict.pyi fix_operator.pyi +fix_except.pyi fix_paren.pyi +fix_exec.pyi fix_print.pyi +fix_execfile.pyi fix_raise.pyi +fix_exitfunc.pyi fix_raw_input.pyi +fix_filter.pyi fix_reduce.pyi +fix_funcattrs.pyi fix_reload.pyi +fix_future.pyi fix_renames.pyi +fix_getcwdu.pyi fix_repr.pyi +fix_has_key.pyi fix_set_literal.pyi +fix_idioms.pyi fix_standarderror.pyi +fix_import.pyi fix_sys_exc.pyi +fix_imports.pyi fix_throw.pyi +fix_imports2.pyi fix_tuple_params.pyi +fix_input.pyi fix_types.pyi +fix_intern.pyi fix_unicode.pyi +fix_isinstance.pyi fix_urllib.pyi +fix_itertools.pyi fix_ws_comma.pyi +fix_itertools_imports.pyi fix_xrange.pyi +fix_long.pyi fix_xreadlines.pyi +fix_map.pyi fix_zip.pyi +fix_metaclass.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/lib2to3/pgen2: +__init__.pyi grammar.pyi parse.pyi token.pyi +driver.pyi literals.pyi pgen.pyi tokenize.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/logging: +__init__.pyi config.pyi handlers.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/msilib: +__init__.pyi schema.pyi sequence.pyi text.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/multiprocessing: +__init__.pyi popen_fork.pyi resource_tracker.pyi +connection.pyi popen_forkserver.pyi shared_memory.pyi +context.pyi popen_spawn_posix.pyi sharedctypes.pyi +dummy popen_spawn_win32.pyi spawn.pyi +forkserver.pyi process.pyi synchronize.pyi +heap.pyi queues.pyi util.pyi +managers.pyi reduction.pyi +pool.pyi resource_sharer.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/multiprocessing/dummy: +__init__.pyi connection.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/os: +__init__.pyi path.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/pathlib: +__init__.pyi types.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/pydoc_data: +__init__.pyi topics.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/pyexpat: +__init__.pyi errors.pyi model.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/sqlite3: +__init__.pyi dbapi2.pyi dump.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/string: +__init__.pyi templatelib.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/sys: +__init__.pyi _monitoring.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/tkinter: +__init__.pyi dnd.pyi simpledialog.pyi +colorchooser.pyi filedialog.pyi tix.pyi +commondialog.pyi font.pyi ttk.pyi +constants.pyi messagebox.pyi +dialog.pyi scrolledtext.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/unittest: +__init__.pyi case.pyi mock.pyi signals.pyi +_log.pyi loader.pyi result.pyi suite.pyi +async_case.pyi main.pyi runner.pyi util.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/urllib: +__init__.pyi parse.pyi response.pyi +error.pyi request.pyi robotparser.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/venv: +__init__.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/wsgiref: +__init__.pyi simple_server.pyi validate.pyi +handlers.pyi types.pyi +headers.pyi util.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml: +__init__.pyi dom etree parsers sax +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml/dom: +NodeFilter.pyi expatbuilder.pyi pulldom.pyi +__init__.pyi minicompat.pyi xmlbuilder.pyi +domreg.pyi minidom.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml/etree: +ElementInclude.pyi ElementTree.pyi cElementTree.pyi +ElementPath.pyi __init__.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml/parsers: +__init__.pyi expat +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml/parsers/expat: +__init__.pyi errors.pyi model.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xml/sax: +__init__.pyi expatreader.pyi saxutils.pyi +_exceptions.pyi handler.pyi xmlreader.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/xmlrpc: +__init__.pyi client.pyi server.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/zipfile: +__init__.pyi _path +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/zipfile/_path: +__init__.pyi glob.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stdlib/zoneinfo: +__init__.pyi _common.pyi _tzpath.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stubs: +librt mypy-extensions +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stubs/librt: +librt +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stubs/librt/librt: +__init__.pyi base64.pyi internal.pyi +./aylm_env/lib/python3.11/site-packages/mypy/typeshed/stubs/mypy-extensions: +mypy_extensions.pyi +./aylm_env/lib/python3.11/site-packages/mypy/xml: +mypy-html.css mypy-html.xslt mypy-txt.xslt mypy.xsd +./aylm_env/lib/python3.11/site-packages/mypy-1.19.1.dist-info: +INSTALLER REQUESTED licenses +METADATA WHEEL top_level.txt +RECORD entry_points.txt +./aylm_env/lib/python3.11/site-packages/mypy-1.19.1.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/mypy_extensions-1.1.0.dist-info: +INSTALLER METADATA RECORD WHEEL licenses +./aylm_env/lib/python3.11/site-packages/mypy_extensions-1.1.0.dist-info/licenses: +LICENSE +./aylm_env/lib/python3.11/site-packages/mypyc: +__init__.cpython-311-darwin.so irbuild +__init__.py lib-rt +__main__.py lower +__pycache__ namegen.cpython-311-darwin.so +analysis namegen.py +annotate.cpython-311-darwin.so options.cpython-311-darwin.so +annotate.py options.py +build.cpython-311-darwin.so primitives +build.py py.typed +build_setup.py rt_subtype.cpython-311-darwin.so +codegen rt_subtype.py +common.cpython-311-darwin.so sametype.cpython-311-darwin.so +common.py sametype.py +crash.cpython-311-darwin.so subtype.cpython-311-darwin.so +crash.py subtype.py +errors.cpython-311-darwin.so test +errors.py transform +ir +./aylm_env/lib/python3.11/site-packages/mypyc/__pycache__: +__init__.cpython-311.pyc errors.cpython-311.pyc +__main__.cpython-311.pyc namegen.cpython-311.pyc +annotate.cpython-311.pyc options.cpython-311.pyc +build.cpython-311.pyc rt_subtype.cpython-311.pyc +build_setup.cpython-311.pyc sametype.cpython-311.pyc +common.cpython-311.pyc subtype.cpython-311.pyc +crash.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/analysis: +__init__.cpython-311-darwin.so capsule_deps.py +__init__.py dataflow.cpython-311-darwin.so +__pycache__ dataflow.py +attrdefined.cpython-311-darwin.so ircheck.cpython-311-darwin.so +attrdefined.py ircheck.py +blockfreq.cpython-311-darwin.so selfleaks.cpython-311-darwin.so +blockfreq.py selfleaks.py +capsule_deps.cpython-311-darwin.so +./aylm_env/lib/python3.11/site-packages/mypyc/analysis/__pycache__: +__init__.cpython-311.pyc dataflow.cpython-311.pyc +attrdefined.cpython-311.pyc ircheck.cpython-311.pyc +blockfreq.cpython-311.pyc selfleaks.cpython-311.pyc +capsule_deps.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/codegen: +__init__.cpython-311-darwin.so emitfunc.cpython-311-darwin.so +__init__.py emitfunc.py +__pycache__ emitmodule.cpython-311-darwin.so +cstring.cpython-311-darwin.so emitmodule.py +cstring.py emitwrapper.cpython-311-darwin.so +emit.cpython-311-darwin.so emitwrapper.py +emit.py literals.cpython-311-darwin.so +emitclass.cpython-311-darwin.so literals.py +emitclass.py +./aylm_env/lib/python3.11/site-packages/mypyc/codegen/__pycache__: +__init__.cpython-311.pyc emitfunc.cpython-311.pyc +cstring.cpython-311.pyc emitmodule.cpython-311.pyc +emit.cpython-311.pyc emitwrapper.cpython-311.pyc +emitclass.cpython-311.pyc literals.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/ir: +__init__.cpython-311-darwin.so module_ir.py +__init__.py ops.cpython-311-darwin.so +__pycache__ ops.py +class_ir.cpython-311-darwin.so pprint.cpython-311-darwin.so +class_ir.py pprint.py +func_ir.cpython-311-darwin.so rtypes.cpython-311-darwin.so +func_ir.py rtypes.py +module_ir.cpython-311-darwin.so +./aylm_env/lib/python3.11/site-packages/mypyc/ir/__pycache__: +__init__.cpython-311.pyc ops.cpython-311.pyc +class_ir.cpython-311.pyc pprint.cpython-311.pyc +func_ir.cpython-311.pyc rtypes.cpython-311.pyc +module_ir.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/irbuild: +__init__.cpython-311-darwin.so +__init__.py +__pycache__ +ast_helpers.cpython-311-darwin.so +ast_helpers.py +builder.cpython-311-darwin.so +builder.py +callable_class.cpython-311-darwin.so +callable_class.py +classdef.cpython-311-darwin.so +classdef.py +constant_fold.cpython-311-darwin.so +constant_fold.py +context.cpython-311-darwin.so +context.py +env_class.cpython-311-darwin.so +env_class.py +expression.cpython-311-darwin.so +expression.py +for_helpers.cpython-311-darwin.so +for_helpers.py +format_str_tokenizer.cpython-311-darwin.so +format_str_tokenizer.py +function.cpython-311-darwin.so +function.py +generator.cpython-311-darwin.so +generator.py +ll_builder.cpython-311-darwin.so +ll_builder.py +main.cpython-311-darwin.so +main.py +mapper.cpython-311-darwin.so +mapper.py +match.cpython-311-darwin.so +match.py +missingtypevisitor.cpython-311-darwin.so +missingtypevisitor.py +nonlocalcontrol.cpython-311-darwin.so +nonlocalcontrol.py +prebuildvisitor.cpython-311-darwin.so +prebuildvisitor.py +prepare.cpython-311-darwin.so +prepare.py +specialize.cpython-311-darwin.so +specialize.py +statement.cpython-311-darwin.so +statement.py +targets.cpython-311-darwin.so +targets.py +util.cpython-311-darwin.so +util.py +visitor.cpython-311-darwin.so +visitor.py +vtable.cpython-311-darwin.so +vtable.py +./aylm_env/lib/python3.11/site-packages/mypyc/irbuild/__pycache__: +__init__.cpython-311.pyc main.cpython-311.pyc +ast_helpers.cpython-311.pyc mapper.cpython-311.pyc +builder.cpython-311.pyc match.cpython-311.pyc +callable_class.cpython-311.pyc missingtypevisitor.cpython-311.pyc +classdef.cpython-311.pyc nonlocalcontrol.cpython-311.pyc +constant_fold.cpython-311.pyc prebuildvisitor.cpython-311.pyc +context.cpython-311.pyc prepare.cpython-311.pyc +env_class.cpython-311.pyc specialize.cpython-311.pyc +expression.cpython-311.pyc statement.cpython-311.pyc +for_helpers.cpython-311.pyc targets.cpython-311.pyc +format_str_tokenizer.cpython-311.pyc util.cpython-311.pyc +function.cpython-311.pyc visitor.cpython-311.pyc +generator.cpython-311.pyc vtable.cpython-311.pyc +ll_builder.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt: +CPy.h librt_internal.c +base64 librt_internal.h +bytes_ops.c list_ops.c +dict_ops.c misc_ops.c +exc_ops.c module_shim.tmpl +float_ops.c module_shim_no_gil_multiphase.tmpl +generic_ops.c mypyc_util.h +getargs.c pythoncapi_compat.h +getargsfast.c pythonsupport.c +init.c pythonsupport.h +int_ops.c set_ops.c +librt_base64.c str_ops.c +librt_base64.h tuple_ops.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64: +arch codecs.h env.h libbase64.h +codec_choose.c config.h lib.c tables +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch: +avx avx2 avx512 generic neon32 neon64 sse41 sse42 ssse3 +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/avx: +codec.c enc_loop_asm.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/avx2: +codec.c dec_reshuffle.c enc_loop_asm.c enc_translate.c +dec_loop.c enc_loop.c enc_reshuffle.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/avx512: +codec.c enc_reshuffle_translate.c +enc_loop.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/generic: +32 codec.c dec_tail.c enc_tail.c +64 dec_head.c enc_head.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/generic/32: +dec_loop.c enc_loop.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/generic/64: +enc_loop.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/neon32: +codec.c dec_loop.c enc_loop.c enc_reshuffle.c enc_translate.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/neon64: +codec.c dec_loop.c enc_loop.c enc_loop_asm.c enc_reshuffle.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/sse41: +codec.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/sse42: +codec.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/arch/ssse3: +codec.c dec_reshuffle.c enc_loop_asm.c enc_translate.c +dec_loop.c enc_loop.c enc_reshuffle.c +./aylm_env/lib/python3.11/site-packages/mypyc/lib-rt/base64/tables: +table_dec_32bit.h tables.c +table_enc_12bit.h tables.h +./aylm_env/lib/python3.11/site-packages/mypyc/lower: +__init__.cpython-311-darwin.so list_ops.py +__init__.py misc_ops.cpython-311-darwin.so +__pycache__ misc_ops.py +int_ops.cpython-311-darwin.so registry.cpython-311-darwin.so +int_ops.py registry.py +list_ops.cpython-311-darwin.so +./aylm_env/lib/python3.11/site-packages/mypyc/lower/__pycache__: +__init__.cpython-311.pyc misc_ops.cpython-311.pyc +int_ops.cpython-311.pyc registry.cpython-311.pyc +list_ops.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/primitives: +__init__.cpython-311-darwin.so list_ops.cpython-311-darwin.so +__init__.py list_ops.py +__pycache__ misc_ops.cpython-311-darwin.so +bytes_ops.cpython-311-darwin.so misc_ops.py +bytes_ops.py registry.cpython-311-darwin.so +dict_ops.cpython-311-darwin.so registry.py +dict_ops.py set_ops.cpython-311-darwin.so +exc_ops.cpython-311-darwin.so set_ops.py +exc_ops.py str_ops.cpython-311-darwin.so +float_ops.cpython-311-darwin.so str_ops.py +float_ops.py tuple_ops.cpython-311-darwin.so +generic_ops.cpython-311-darwin.so tuple_ops.py +generic_ops.py weakref_ops.cpython-311-darwin.so +int_ops.cpython-311-darwin.so weakref_ops.py +int_ops.py +./aylm_env/lib/python3.11/site-packages/mypyc/primitives/__pycache__: +__init__.cpython-311.pyc list_ops.cpython-311.pyc +bytes_ops.cpython-311.pyc misc_ops.cpython-311.pyc +dict_ops.cpython-311.pyc registry.cpython-311.pyc +exc_ops.cpython-311.pyc set_ops.cpython-311.pyc +float_ops.cpython-311.pyc str_ops.cpython-311.pyc +generic_ops.cpython-311.pyc tuple_ops.cpython-311.pyc +int_ops.cpython-311.pyc weakref_ops.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/test: +__init__.py test_emitfunc.py test_optimizations.py +__pycache__ test_emitwrapper.py test_pprint.py +config.py test_exceptions.py test_rarray.py +test_alwaysdefined.py test_external.py test_refcount.py +test_analysis.py test_irbuild.py test_run.py +test_annotate.py test_ircheck.py test_serialization.py +test_cheader.py test_literals.py test_struct.py +test_commandline.py test_lowering.py test_tuplename.py +test_emit.py test_misc.py test_typeops.py +test_emitclass.py test_namegen.py testutil.py +./aylm_env/lib/python3.11/site-packages/mypyc/test/__pycache__: +__init__.cpython-311.pyc test_literals.cpython-311.pyc +config.cpython-311.pyc test_lowering.cpython-311.pyc +test_alwaysdefined.cpython-311.pyc test_misc.cpython-311.pyc +test_analysis.cpython-311.pyc test_namegen.cpython-311.pyc +test_annotate.cpython-311.pyc test_optimizations.cpython-311.pyc +test_cheader.cpython-311.pyc test_pprint.cpython-311.pyc +test_commandline.cpython-311.pyc test_rarray.cpython-311.pyc +test_emit.cpython-311.pyc test_refcount.cpython-311.pyc +test_emitclass.cpython-311.pyc test_run.cpython-311.pyc +test_emitfunc.cpython-311.pyc test_serialization.cpython-311.pyc +test_emitwrapper.cpython-311.pyc test_struct.cpython-311.pyc +test_exceptions.cpython-311.pyc test_tuplename.cpython-311.pyc +test_external.cpython-311.pyc test_typeops.cpython-311.pyc +test_irbuild.cpython-311.pyc testutil.cpython-311.pyc +test_ircheck.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/mypyc/transform: +__init__.cpython-311-darwin.so log_trace.cpython-311-darwin.so +__init__.py log_trace.py +__pycache__ lower.cpython-311-darwin.so +copy_propagation.cpython-311-darwin.so lower.py +copy_propagation.py refcount.cpython-311-darwin.so +exceptions.cpython-311-darwin.so refcount.py +exceptions.py spill.cpython-311-darwin.so +flag_elimination.cpython-311-darwin.so spill.py +flag_elimination.py uninit.cpython-311-darwin.so +ir_transform.cpython-311-darwin.so uninit.py +ir_transform.py +./aylm_env/lib/python3.11/site-packages/mypyc/transform/__pycache__: +__init__.cpython-311.pyc log_trace.cpython-311.pyc +copy_propagation.cpython-311.pyc lower.cpython-311.pyc +exceptions.cpython-311.pyc refcount.cpython-311.pyc +flag_elimination.cpython-311.pyc spill.cpython-311.pyc +ir_transform.cpython-311.pyc uninit.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals: +__init__.py _sql group_by.py +__pycache__ _translate.py plugins.py +_arrow _typing.py py.typed +_compliant _typing_compat.py schema.py +_constants.py _utils.py selectors.py +_dask compliant.py series.py +_duckdb dataframe.py series_cat.py +_duration.py dependencies.py series_dt.py +_enum.py dtypes.py series_list.py +_exceptions.py exceptions.py series_str.py +_expression_parsing.py expr.py series_struct.py +_ibis expr_cat.py sql.py +_interchange expr_dt.py stable +_namespace.py expr_list.py testing +_native.py expr_name.py this.py +_pandas_like expr_str.py translate.py +_polars expr_struct.py typing.py +_spark_like functions.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/__pycache__: +__init__.cpython-311.pyc expr_list.cpython-311.pyc +_constants.cpython-311.pyc expr_name.cpython-311.pyc +_duration.cpython-311.pyc expr_str.cpython-311.pyc +_enum.cpython-311.pyc expr_struct.cpython-311.pyc +_exceptions.cpython-311.pyc functions.cpython-311.pyc +_expression_parsing.cpython-311.pyc group_by.cpython-311.pyc +_namespace.cpython-311.pyc plugins.cpython-311.pyc +_native.cpython-311.pyc schema.cpython-311.pyc +_translate.cpython-311.pyc selectors.cpython-311.pyc +_typing.cpython-311.pyc series.cpython-311.pyc +_typing_compat.cpython-311.pyc series_cat.cpython-311.pyc +_utils.cpython-311.pyc series_dt.cpython-311.pyc +compliant.cpython-311.pyc series_list.cpython-311.pyc +dataframe.cpython-311.pyc series_str.cpython-311.pyc +dependencies.cpython-311.pyc series_struct.cpython-311.pyc +dtypes.cpython-311.pyc sql.cpython-311.pyc +exceptions.cpython-311.pyc this.cpython-311.pyc +expr.cpython-311.pyc translate.cpython-311.pyc +expr_cat.cpython-311.pyc typing.cpython-311.pyc +expr_dt.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_arrow: +__init__.py namespace.py series_list.py +__pycache__ selectors.py series_str.py +dataframe.py series.py series_struct.py +expr.py series_cat.py typing.py +group_by.py series_dt.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/_arrow/__pycache__: +__init__.cpython-311.pyc series_cat.cpython-311.pyc +dataframe.cpython-311.pyc series_dt.cpython-311.pyc +expr.cpython-311.pyc series_list.cpython-311.pyc +group_by.cpython-311.pyc series_str.cpython-311.pyc +namespace.cpython-311.pyc series_struct.cpython-311.pyc +selectors.cpython-311.pyc typing.cpython-311.pyc +series.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_compliant: +__init__.py dataframe.py selectors.py +__pycache__ expr.py series.py +any_namespace.py group_by.py typing.py +column.py namespace.py window.py +./aylm_env/lib/python3.11/site-packages/narwhals/_compliant/__pycache__: +__init__.cpython-311.pyc namespace.cpython-311.pyc +any_namespace.cpython-311.pyc selectors.cpython-311.pyc +column.cpython-311.pyc series.cpython-311.pyc +dataframe.cpython-311.pyc typing.cpython-311.pyc +expr.cpython-311.pyc window.cpython-311.pyc +group_by.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_dask: +__init__.py dataframe.py expr_dt.py group_by.py selectors.py +__pycache__ expr.py expr_str.py namespace.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/_dask/__pycache__: +__init__.cpython-311.pyc group_by.cpython-311.pyc +dataframe.cpython-311.pyc namespace.cpython-311.pyc +expr.cpython-311.pyc selectors.cpython-311.pyc +expr_dt.cpython-311.pyc utils.cpython-311.pyc +expr_str.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_duckdb: +__init__.py expr.py expr_str.py namespace.py typing.py +__pycache__ expr_dt.py expr_struct.py selectors.py utils.py +dataframe.py expr_list.py group_by.py series.py +./aylm_env/lib/python3.11/site-packages/narwhals/_duckdb/__pycache__: +__init__.cpython-311.pyc group_by.cpython-311.pyc +dataframe.cpython-311.pyc namespace.cpython-311.pyc +expr.cpython-311.pyc selectors.cpython-311.pyc +expr_dt.cpython-311.pyc series.cpython-311.pyc +expr_list.cpython-311.pyc typing.cpython-311.pyc +expr_str.cpython-311.pyc utils.cpython-311.pyc +expr_struct.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_ibis: +__init__.py expr.py expr_str.py namespace.py utils.py +__pycache__ expr_dt.py expr_struct.py selectors.py +dataframe.py expr_list.py group_by.py series.py +./aylm_env/lib/python3.11/site-packages/narwhals/_ibis/__pycache__: +__init__.cpython-311.pyc expr_struct.cpython-311.pyc +dataframe.cpython-311.pyc group_by.cpython-311.pyc +expr.cpython-311.pyc namespace.cpython-311.pyc +expr_dt.cpython-311.pyc selectors.cpython-311.pyc +expr_list.cpython-311.pyc series.cpython-311.pyc +expr_str.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_interchange: +__init__.py __pycache__ dataframe.py series.py +./aylm_env/lib/python3.11/site-packages/narwhals/_interchange/__pycache__: +__init__.cpython-311.pyc series.cpython-311.pyc +dataframe.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_pandas_like: +__init__.py namespace.py series_list.py +__pycache__ selectors.py series_str.py +dataframe.py series.py series_struct.py +expr.py series_cat.py typing.py +group_by.py series_dt.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/_pandas_like/__pycache__: +__init__.cpython-311.pyc series_cat.cpython-311.pyc +dataframe.cpython-311.pyc series_dt.cpython-311.pyc +expr.cpython-311.pyc series_list.cpython-311.pyc +group_by.cpython-311.pyc series_str.cpython-311.pyc +namespace.cpython-311.pyc series_struct.cpython-311.pyc +selectors.cpython-311.pyc typing.cpython-311.pyc +series.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_polars: +__init__.py dataframe.py group_by.py series.py utils.py +__pycache__ expr.py namespace.py typing.py +./aylm_env/lib/python3.11/site-packages/narwhals/_polars/__pycache__: +__init__.cpython-311.pyc namespace.cpython-311.pyc +dataframe.cpython-311.pyc series.cpython-311.pyc +expr.cpython-311.pyc typing.cpython-311.pyc +group_by.cpython-311.pyc utils.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_spark_like: +__init__.py expr.py expr_str.py namespace.py +__pycache__ expr_dt.py expr_struct.py selectors.py +dataframe.py expr_list.py group_by.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/_spark_like/__pycache__: +__init__.cpython-311.pyc expr_struct.cpython-311.pyc +dataframe.cpython-311.pyc group_by.cpython-311.pyc +expr.cpython-311.pyc namespace.cpython-311.pyc +expr_dt.cpython-311.pyc selectors.cpython-311.pyc +expr_list.cpython-311.pyc utils.cpython-311.pyc +expr_str.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/_sql: +__init__.py dataframe.py expr_dt.py group_by.py typing.py +__pycache__ expr.py expr_str.py namespace.py +./aylm_env/lib/python3.11/site-packages/narwhals/_sql/__pycache__: +__init__.cpython-311.pyc expr_str.cpython-311.pyc +dataframe.cpython-311.pyc group_by.cpython-311.pyc +expr.cpython-311.pyc namespace.cpython-311.pyc +expr_dt.cpython-311.pyc typing.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/stable: +__init__.py __pycache__ v1 v2 +./aylm_env/lib/python3.11/site-packages/narwhals/stable/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/stable/v1: +__init__.py _dtypes.py dependencies.py selectors.py +__pycache__ _namespace.py dtypes.py typing.py +./aylm_env/lib/python3.11/site-packages/narwhals/stable/v1/__pycache__: +__init__.cpython-311.pyc dtypes.cpython-311.pyc +_dtypes.cpython-311.pyc selectors.cpython-311.pyc +_namespace.cpython-311.pyc typing.cpython-311.pyc +dependencies.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/stable/v2: +__init__.py _namespace.py dtypes.py typing.py +__pycache__ dependencies.py selectors.py +./aylm_env/lib/python3.11/site-packages/narwhals/stable/v2/__pycache__: +__init__.cpython-311.pyc dtypes.cpython-311.pyc +_namespace.cpython-311.pyc selectors.cpython-311.pyc +dependencies.cpython-311.pyc typing.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/testing: +__init__.py __pycache__ asserts +./aylm_env/lib/python3.11/site-packages/narwhals/testing/__pycache__: +__init__.cpython-311.pyc +./aylm_env/lib/python3.11/site-packages/narwhals/testing/asserts: +__init__.py __pycache__ frame.py series.py utils.py +./aylm_env/lib/python3.11/site-packages/narwhals/testing/asserts/__pycache__: +LINE LIMIT EXCEEDED \ No newline at end of file diff --git a/README.md b/README.md index 97ee752..cfcf8ea 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,421 @@ -# A.YLM +# A-YLM: Geometric Constitutional AI for Embodied Intelligence -Single-image 3D reconstruction and intelligent navigation system based on Apple SHARP model. +
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/) +[![arXiv](https://img.shields.io/badge/arXiv-2026.xxxxx-b31b1b.svg)](https://arxiv.org) -## Features +**Self-Supervised Safety Framework | Autonomous Driving | Robotics | Embodied AI** -- **Single-Image 3D Reconstruction**: Convert a single RGB image into a 3D Gaussian Splatting model using Apple's SHARP Vision Transformer -- **Semantic Fusion**: YOLO-based object detection with 3D point cloud semantic labeling -- **Obstacle Detection**: Automatic identification of vehicles, pedestrians, bicycles, and other obstacles -- **Navigation Mesh Generation**: Generate robot-navigable 3D voxel meshes with obstacle information -- **Pipeline Processing**: Parallel processing for multiple images (inference + voxelization) -- **Video Processing**: Extract frames from video and process as image sequence -- **Ground Detection**: RANSAC-based ground plane detection with normal vector validation -- **Coordinate Transformation**: OpenCV to Robot/ENU coordinate system conversion -- **Multi-Format Support**: 68+ image formats including HEIC, JPEG, PNG, WEBP, AVIF, TIFF, PSD +*Extending Anthropic's Constitutional AI paradigm from language to the physical world* -## System Requirements +[Paper](#) | [Documentation](#) | [Demo](#) | [中文文档](docs/paper_aylm_zh.md) -- Python 3.9+ (Python 3.11 recommended for Open3D compatibility) -- PyTorch 2.0+ -- 4GB+ RAM -- GPU recommended (supports CUDA, MPS for Apple Silicon) +
-### Optional Dependencies +--- + +## Vision: Physical Constitution for AI + +> *"Just as Constitutional AI teaches language models ethical boundaries through textual principles, A-YLM teaches embodied AI systems physical boundaries through geometric constraints."* + +A-YLM represents a paradigm shift in AI safety — from **reactive safety checks** to **constitutional safety learning**. We believe that for AI to truly understand and safely navigate the physical world, it needs more than perception; it needs a **geometric constitution** that defines the fundamental laws of physical interaction. + +--- + +## Abstract + +A-YLM is a **Geometric Constitutional AI** framework for embodied intelligence systems. Inspired by Anthropic's Constitutional AI approach for language models, A-YLM extends this safety paradigm to the **physical world** — providing geometric constraints as a "Physical Constitution" that governs AI behavior in 3D space. + +The core philosophy: **AI needs physical laws, not just language rules**. While Constitutional AI teaches language models ethical boundaries through textual principles, A-YLM teaches embodied AI systems physical boundaries through geometric constraints. This enables AI to **understand the world** through self-supervised learning from massive geometric data. + +### Key Innovations + +| Concept | Language AI (Constitutional AI) | Embodied AI (A-YLM) | +|---------|--------------------------------|---------------------| +| **Constitution** | Ethical principles in text | Geometric constraints in 3D | +| **Supervision** | RLHF with human feedback | Self-supervised geometric validation | +| **Learning** | Learn from text corrections | Learn from physical violations | +| **Safety** | Prevent harmful outputs | Prevent unsafe physical actions | +| **Evolution** | Iterative refinement | Continuous geometric feedback | + +### Core Capabilities + +- **Geometric Constitutional Supervision**: Physical laws as AI behavior boundaries +- **Self-Supervised World Understanding**: AI learns physics through geometric feedback — no human labeling required +- **Embodied Intelligence Training**: Continuous self-evolution from massive 3D data +- **Universal Applicability**: Not just autonomous driving — all embodied AI systems (robotics, drones, AR/VR, humanoids) +- **Edge-Deployable**: Real-time geometric supervision on resource-constrained devices + +The system pipeline: **Sensor Input → 3DGS Reconstruction → Voxelization → Constitutional Validation → Geometric Feedback → Self-Learning**. + +--- + +## Why Geometric Constitutional AI? + +### The Problem with Current AI Safety + +Current embodied AI systems face a fundamental challenge: + +| Approach | Limitation | +|----------|------------| +| **Rule-based Safety** | Cannot generalize to novel situations | +| **Learned Safety** | Requires massive human-labeled data | +| **End-to-End Models** | Black-box decisions, uninterpretable | +| **Simulation-based** | Sim-to-real gap, limited coverage | + +### Our Solution: Bidirectional Fusion with E2E Systems + +A-YLM introduces **Geometric Constitutional AI** with **bidirectional fusion** — fully compatible with existing end-to-end driving systems (Tesla FSD, Huawei ADS, etc.): + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Bidirectional Fusion Architecture │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ End-to-End Driving AI (FSD/ADS) │ │ +│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ Video │───▶│ Neural │───▶│ Decision │───▶│ Control │ │ │ +│ │ │ Input │ │ Network │ │ Output │ │ Signal │ │ │ +│ │ └──────────┘ └────┬─────┘ └────┬─────┘ └──────────┘ │ │ +│ │ │ │ │ │ +│ │ │ 3D Input │ Decision │ │ +│ │ ▼ ▼ │ │ +│ └────────────────────────┼───────────────┼────────────────────────────┘ │ +│ │ │ │ +│ ┌────────────────────────┼───────────────┼────────────────────────────┐ │ +│ │ A-YLM Edge Module (Lightweight) │ │ +│ │ │ │ │ │ +│ │ ┌──────────┐ ┌────▼─────┐ ┌────▼─────┐ ┌──────────┐ │ │ +│ │ │ Camera │───▶│ 3D GS │───▶│ Safety │───▶│ Training │ │ │ +│ │ │ Input │ │ Recon │ │ Scoring │ │ Signal │ │ │ +│ │ └──────────┘ └────┬─────┘ └──────────┘ └──────────┘ │ │ +│ │ │ │ │ +│ │ │ Point Cloud + Color │ │ +│ │ ▼ │ │ +│ │ ┌──────────────┐ │ │ +│ │ │ 3D Scene │ ──▶ Available for E2E AI Input │ │ +│ │ │ with Color │ │ │ +│ │ └──────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +**Direction 1: Safety Supervision (A-YLM → E2E AI)** +- A-YLM monitors and validates E2E AI decisions in real-time +- Provides safety scores and violation labels +- Generates training signals for AI self-improvement (no human labeling) + +**Direction 2: 3D Input Enhancement (A-YLM → E2E AI)** +- A-YLM provides 3D point cloud as additional input to E2E AI +- Includes color information from 3D Gaussian Splatting +- Enables E2E AI to perceive 3D geometry directly +- Fully compatible with existing video-based E2E architectures + +**Key Advantages:** +1. **Full Compatibility**: Works with existing E2E systems (FSD, ADS) without modification +2. **Enhanced Perception**: E2E AI gains 3D geometric understanding +3. **Local Safety Guarantee**: Edge module provides real-time safety validation +4. **Lightweight & Edge-Optimized**: Runs on Jetson, Apple MPS, and other edge devices +5. **Self-Supervised Learning**: AI evolves through geometric feedback, no human annotation needed + +### Local Safety Decision Module + +A-YLM provides a **standalone local safety decision module** that can be called as an independent component: + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ A-YLM Local Safety Decision Module │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Input: Camera Frame │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Camera │───▶│ 3D GS │───▶│ Point │───▶│ Depth │ │ +│ │ Frame │ │ Recon │ │ Cloud │ │ Info │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ Safety Scoring Module (Our Contribution) │ │ +│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ Collision │ │ TTC │ │ Boundary │ │ │ +│ │ │ Detection │ │ Calculation │ │ Validation │ │ │ +│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ +│ │ │ │ │ │ │ +│ │ └─────────────────┼─────────────────┘ │ │ +│ │ ▼ │ │ +│ │ ┌──────────────┐ │ │ +│ │ │ Safety Score │ ──▶ 0.0 (Dangerous) ~ 1.0 (Safe) │ │ +│ │ └──────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ Output: ▼ │ +│ ┌──────────────────────────────────────────────────────────────────────┐ │ +│ │ - Safety Score (0.0 ~ 1.0) │ │ +│ │ - Violation Labels (collision, ttc_warning, boundary) │ │ +│ │ - 3D Obstacle Positions with Depth │ │ +│ │ - Recommended Action (safe/warning/emergency_stop) │ │ +│ │ - Training Signal for Cloud AI │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +**Module Features:** +- **3D Decision Making**: With point cloud and depth information, enables 3D-based safety decisions locally +- **Safety Scoring**: Our proposed scoring module evaluates AI decisions against geometric constraints +- **Modular Design**: Can be called as an independent module by any system +- **Real-time**: Optimized for edge deployment with <50ms latency +- **API-Ready**: Simple Python API for integration + +```python +# Example: Using A-YLM as a safety module +from aylm.safety import SafetyModule + +safety = SafetyModule() + +# Get safety score for an AI decision +result = safety.evaluate( + camera_frame=frame, + ai_decision={"steering": 0.1, "acceleration": 0.5} +) + +print(f"Safety Score: {result.score}") # 0.0 ~ 1.0 +print(f"Violations: {result.violations}") # ['ttc_warning'] +print(f"Recommended: {result.action}") # 'reduce_speed' +print(f"Training Signal: {result.training_label}") # For cloud AI +``` + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Geometric Constitutional AI Loop │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ AI │ ──────▶ │ Geometric │ ──────▶ │ Physical │ │ +│ │ Decision │ │ Validation │ │ Execution │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ ▲ │ │ │ +│ │ │ Violation? │ │ +│ │ ▼ │ │ +│ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ World │ ◀────── │ Feedback │ ◀─────────────┘ │ +│ │ Model │ │ Generation │ Physical Outcome │ +│ │ Update │ └─────────────┘ │ +│ └─────────────┘ │ +│ │ │ +│ └──────────────── Self-Evolution ────────────────────────────────│ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1. Geometric Constitutional AI: A New Safety Paradigm + +### 1.1 From Language Constitution to Physical Constitution + +Anthropic's Constitutional AI revolutionized language model safety by embedding ethical principles directly into the training process. A-YLM extends this paradigm to **embodied intelligence**: + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Constitutional AI Paradigm Comparison │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Language AI (Anthropic) Embodied AI (A-YLM) │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ Text Constitution │ │ Geometric Constitution│ │ +│ │ "Be helpful, │ │ "Respect physical │ │ +│ │ harmless, honest" │ │ boundaries, avoid │ │ +│ └─────────────────────┘ │ collisions, obey │ │ +│ │ │ spatial constraints"│ │ +│ ▼ └─────────────────────┘ │ +│ ┌─────────────────────┐ │ │ +│ │ RLHF Training │ ▼ │ +│ │ (Human Feedback) │ ┌─────────────────────┐ │ +│ └─────────────────────┘ │ Self-Supervised │ │ +│ │ │ Geometric Learning │ │ +│ ▼ └─────────────────────┘ │ +│ ┌─────────────────────┐ │ │ +│ │ Safe Language │ ▼ │ +│ │ Outputs │ ┌─────────────────────┐ │ +│ └─────────────────────┘ │ Safe Physical │ │ +│ │ Actions │ │ +│ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### 1.2 Self-Supervised World Understanding (Embodied Intelligence) + +A-YLM enables AI to **understand the physical world** through continuous geometric learning — the essence of **embodied intelligence**: + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Self-Supervised World Understanding │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ Stage 1: Geometric Data Collection │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ Camera → 3DGS Reconstruction → Voxel Grid → Physical Ground Truth │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Stage 2: Constitutional Validation │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ AI Decision → Geometric Check → Collision? Boundary? TTC? │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Stage 3: Violation Detection & Feedback │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ Violation Detected → Generate Training Signal → Update World Model │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ Stage 4: AI Self-Evolution │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ Improved World Understanding → Better Decisions → Safer Actions │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +This creates a **self-improving loop** where AI learns physics without human labeling — true embodied intelligence. + +### 1.3 Universal Embodied Intelligence Applications + +While autonomous driving is our primary demonstration, A-YLM's geometric constitutional approach applies to **all embodied AI**: + +| Domain | Application | Geometric Constitution | Self-Learning Signal | +|--------|-------------|------------------------|----------------------| +| **Autonomous Driving** | Vehicle safety | Collision avoidance, lane boundaries | Near-miss detection | +| **Robotics** | Manipulation safety | Workspace limits, force constraints | Contact detection | +| **Drones/UAV** | Flight safety | Obstacle avoidance, no-fly zones | Proximity alerts | +| **AR/VR** | Spatial interaction | Physical object boundaries | Occlusion conflicts | +| **Humanoid Robots** | Navigation safety | Human proximity, obstacle clearance | Social distance violations | +| **Industrial Automation** | Workspace safety | Equipment boundaries, safety zones | Zone intrusion | + +--- + +## 2. Technical Architecture + +### 2.1 Geometric Safety Supervisor + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ A-YLM Safety Validation Pipeline │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │ +│ │ RGB │ │ SHARP │ │ Voxel │ │ Semantic │ │ +│ │ Input │───▶│ 3DGS │───▶│ Grid │───▶│ Fusion │ │ +│ │ (Multi- │ │ Reconstruct │ │ (Occupancy │ │ (YOLO + │ │ +│ │ view) │ │ │ │ 2.0) │ │ 3D Proj) │ │ +│ └──────────┘ └──────────────┘ └─────────────┘ └──────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────────────────────┐ │ +│ │ Safety Validation Module │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Obstacle │ │ Collision │ │ Safety │ │ Geometric │ │ │ +│ │ │ Detection │ │ Prediction │ │ Boundary │ │ Feedback │ │ │ +│ │ │ (DBSCAN) │ │ (TTC) │ │ Validation │ │ Output │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ └──────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### 2.2 Processing Stages + +| Stage | Module | Input | Output | Purpose | +|-------|--------|-------|--------|---------| +| 1 | 3D Reconstruction | RGB Image | 3D Gaussian Splatting | Physical Ground Truth | +| 2 | Voxelization | 3DGS Point Cloud | Occupancy Grid | Geometric Representation | +| 3 | Ground Removal | Voxel Grid | Filtered Grid | Obstacle Isolation | +| 4 | Object Detection | RGB Image | 2D Bounding Boxes | Semantic Understanding | +| 5 | Semantic Fusion | 2D + 3D | Labeled Point Cloud | Safety Context | +| 6 | Safety Validation | Semantic 3D | Safety Decisions | Geometric Verification | +| 7 | Feedback Generation | Validation Results | Learning Signals | Self-Evolution | + +--- + +## 3. Core Innovations + +### 3.1 Physical Ground Truth via 3DGS + +The 3DGS representation provides **physical ground truth** for safety validation: + +``` +Raw Sensor Data → 3D Gaussian Splatting → Voxel Occupancy Grid → Safety Validation + ↑ ↑ + Physical Geometric + Reality Constraint +``` + +Benefits: +- **Interpretable**: Each Gaussian corresponds to physical scene elements +- **Verifiable**: Intermediate outputs can be visualized and validated +- **Geometric**: Explicit 3D structure enables physics-based safety reasoning -| Feature | Dependency | Installation | -|---------|------------|--------------| -| Fast voxelization | Open3D | `pip install open3d` | -| Object detection | Ultralytics YOLO | `pip install ultralytics` | -| Video processing | OpenCV | `pip install opencv-python` | +### 3.2 Edge-Deployed Safety Module -## Installation +A-YLM is designed for **edge deployment** alongside E2E models: -### Quick Start +| Representation | Data Size | Inference Complexity | +|----------------|-----------|----------------------| +| Raw 3DGS | ~500K Gaussians | O(n²) rendering | +| Voxel Grid (5cm) | ~50K voxels | O(n) lookup | +| Sparse Voxel | ~10K occupied | O(k) where k << n | + +This compression enables **real-time safety validation** on edge computing platforms. + +### 3.3 Self-Supervised Safety Learning + +The system learns safety boundaries without manual labeling: + +1. **Geometric Constraint Mining**: Extract safety rules from 3D structure +2. **Violation Detection**: Identify when E2E decisions violate geometric constraints +3. **Feedback Generation**: Produce learning signals for model improvement + +### 3.4 Apple SHARP Integration + +We leverage Apple's **SHARP** model for efficient 3D reconstruction: + +- **Single-image 3D reconstruction**: Eliminates multi-view requirements +- **Edge-optimized inference**: Designed for Apple Silicon (MPS acceleration) +- **Real-time capable**: Suitable for safety-critical applications + +--- + +## 4. System Requirements + +### 4.1 Hardware Requirements + +| Component | Minimum | Recommended | +|-----------|---------|-------------| +| RAM | 4GB | 16GB | +| GPU | - | CUDA 11.0+ / Apple MPS | +| Storage | 5GB | 20GB (with models) | + +### 4.2 Software Dependencies + +| Dependency | Version | Purpose | +|------------|---------|---------| +| Python | 3.9+ (3.11 recommended) | Runtime | +| PyTorch | 2.0+ | Deep Learning Framework | +| Open3D | 0.17+ | Point Cloud Processing | +| Ultralytics | 8.0+ | YOLO Object Detection | +| NumPy | 1.24+ | Numerical Computing | + +--- + +## 5. Installation + +### 5.1 Quick Start ```bash # Clone repository with submodules @@ -45,95 +426,92 @@ cd A.YLM python3.11 -m venv aylm_env source aylm_env/bin/activate -# Install dependencies +# Install core dependencies pip install -e . pip install -e ml-sharp/ -# Install with all features (Open3D + YOLO) +# Install full feature set pip install -e ".[full]" ``` -### YOLO Model Setup - -For semantic fusion and obstacle detection, download YOLO model: +### 5.2 Model Setup ```bash -# Auto-download on first use, or manually: -pip install ultralytics +# SHARP model (~2.8GB) - auto-downloads on first use +aylm setup --download + +# YOLO model (~6MB) - auto-downloads on first use python -c "from ultralytics import YOLO; YOLO('yolo11n-seg.pt')" ``` -### Development Installation - -```bash -pip install -e ".[dev]" -``` +--- -## Usage +## 6. Usage -### One-Click Run +### 6.1 One-Click Execution ```bash -# Run complete workflow: setup + predict + voxelize +# Complete safety validation workflow ./run.sh -# Or with custom input +# Custom input directory ./run.sh --input /path/to/images -# Force pipeline mode for multiple images +# Force pipeline mode for batch processing ./run.sh --pipeline ``` -### CLI Commands +### 6.2 CLI Commands ```bash # Environment setup and model download aylm setup --download -# Run SHARP prediction (image to 3D Gaussian) +# Stage 1: SHARP 3D Reconstruction aylm predict -i inputs/input_images -o outputs/output_gaussians -v -# Run voxelization (3D Gaussian to voxel grid) +# Stage 2: Voxelization (Occupancy 2.0) aylm voxelize -i outputs/output_gaussians --voxel-size 0.005 -# Run complete pipeline (sequential) +# Stage 3: Complete Pipeline (Sequential) aylm process -v -# Run parallel pipeline (for multiple images) +# Stage 4: Parallel Pipeline (Multi-image) aylm pipeline -i inputs/input_images -o outputs/output_gaussians -v ``` -### Video Processing +### 6.3 Video Processing ```bash -# Extract frames from video +# Frame extraction aylm video extract -i video.mp4 -o frames/ --interval 1.0 -# Process video (extract + inference + voxelize) +# Full video pipeline (extract + inference + voxelize + tracking) aylm video process -i video.mp4 -o output/ --use-gpu -# Play voxel sequence +# Visualization playback aylm video play -i voxels/ --fps 10 --loop ``` -### Python API +### 6.4 Python API ```python from aylm.tools.pointcloud_voxelizer import PointCloudVoxelizer, VoxelizerConfig from aylm.tools.semantic_fusion import SemanticFusion from aylm.tools.object_detector import ObjectDetector +from aylm.tools.pipeline_processor import PipelineProcessor, PipelineConfig -# Basic voxelization -config = VoxelizerConfig(voxel_size=0.005) # 5mm voxels -processor = PointCloudVoxelizer(config=config) -processor.process( - input_path="output.ply", +# Stage 1: Voxelization with Occupancy 2.0 +config = VoxelizerConfig(voxel_size=0.005) # 5mm resolution +voxelizer = PointCloudVoxelizer(config=config) +voxelizer.process( + input_path="gaussians.ply", output_path="voxelized.ply", - remove_ground=True + remove_ground=True # RANSAC ground plane removal ) -# Semantic fusion with obstacle detection -detector = ObjectDetector() +# Stage 2: Semantic Fusion (2D → 3D Projection) +detector = ObjectDetector(model_name="yolo11n-seg.pt") detections = detector.detect("image.png") fusion = SemanticFusion() @@ -143,156 +521,180 @@ semantic_pc = fusion.fuse_semantics( detections=detections ) -# Save navigation mesh (5cm voxels) -fusion.save_navigation_ply(semantic_pc, "navigation.ply", voxel_size=0.05) -``` +# Stage 3: Safety Validation Output +fusion.save_navigation_ply(semantic_pc, "safety_validation.ply", voxel_size=0.05) -### Pipeline Processing - -```python -from aylm.tools.pipeline_processor import PipelineProcessor, PipelineConfig - -# Configure pipeline -config = PipelineConfig( +# Full Pipeline Processing +pipeline_config = PipelineConfig( verbose=True, enable_semantic_fusion=True, output_navigation_ply=True ) - -# Process multiple images in parallel -processor = PipelineProcessor(config) +processor = PipelineProcessor(pipeline_config) results = processor.run_pipeline( input_dir="inputs/input_images", output_dir="outputs" ) ``` -## Project Structure +--- -``` -A.YLM/ -├── src/aylm/ # Main package -│ ├── cli.py # Command line interface -│ └── tools/ # Processing tools -│ ├── pointcloud_voxelizer.py # Voxelization module -│ ├── pipeline_processor.py # Parallel pipeline -│ ├── video_pipeline.py # Video processing -│ ├── semantic_fusion.py # Semantic labeling -│ ├── object_detector.py # YOLO detection -│ ├── obstacle_marker.py # Obstacle marking -│ ├── pointcloud_slicer.py # Point cloud slicing -│ └── coordinate_utils.py # Coordinate transforms -├── ml-sharp/ # Apple SHARP model (submodule) -├── models/ # Model checkpoints -├── inputs/ -│ ├── input_images/ # Input images -│ └── videos/ # Input videos -├── outputs/ -│ ├── output_gaussians/ # 3D Gaussian PLY files -│ ├── voxelized/ # Voxelized point clouds -│ ├── detections/ # Detection results (JSON) -│ └── navigation/ # Navigation meshes -├── tests/ # Test suite -├── run.sh # One-click run script -└── pyproject.toml # Project configuration -``` +## 7. Output Specification -## Output Files +### 7.1 File Formats -| File | Description | -|------|-------------| -| `*.ply` | 3D Gaussian Splatting model from SHARP | -| `vox_*.ply` | Voxelized point cloud with semantic colors | -| `vox_*_obstacles.json` | Obstacle detection results | -| `nav_*.ply` | Navigation mesh (5cm solid cubes) | +| File Pattern | Format | Description | +|--------------|--------|-------------| +| `*.ply` | PLY | Raw 3DGS point cloud from SHARP | +| `vox_*.ply` | PLY | Voxelized point cloud with semantic colors | +| `nav_*.ply` | PLY | Safety validation mesh (5cm solid cubes) | +| `*_obstacles.json` | JSON | Structured obstacle data for safety validation | -### Obstacle JSON Format +### 7.2 Safety Validation JSON Schema ```json { - "coordinate_systems": { - "cv": {"axes": "X右, Y下, Z前"}, - "robot": {"axes": "X前, Y左, Z上"} + "metadata": { + "timestamp": "2026-02-27T10:30:00Z", + "frame_id": 42, + "coordinate_systems": { + "cv": {"axes": "X-right, Y-down, Z-forward"}, + "robot": {"axes": "X-forward, Y-left, Z-up (ENU)"} + } }, "obstacles": [ { - "type": "可运动障碍物", - "category": "车辆", + "id": 1, + "type": "dynamic", + "category": "vehicle", "center_cv": [10.32, 0.06, 0.21], "center_robot": [0.21, -10.32, -0.06], - "dimensions_cv": [7.52, 3.36, 2.26], - "confidence": 0.93 + "dimensions": [7.52, 3.36, 2.26], + "velocity": [2.5, 0.0, 0.1], + "ttc": 4.2, + "confidence": 0.93, + "safety_status": "warning" } - ] + ], + "safety_summary": { + "overall_status": "safe", + "collision_risk": 0.15, + "geometric_violations": [] + } } ``` -## Configuration +### 7.3 Semantic Label Taxonomy + +| Label ID | Category | Color (RGB) | Motion Type | +|----------|----------|-------------|-------------| +| 0 | GROUND | (139, 69, 19) | Static | +| 1 | VEHICLE | (0, 0, 255) | Dynamic | +| 2 | PEDESTRIAN | (255, 0, 0) | Dynamic | +| 3 | BICYCLE | (0, 255, 255) | Dynamic | +| 4 | BUILDING | (128, 128, 128) | Static | +| 5 | VEGETATION | (0, 255, 0) | Static | +| 255 | UNKNOWN | (255, 255, 255) | Unknown | -### Environment Variables +--- + +## 8. Configuration + +### 8.1 Environment Variables ```bash -export AYLM_ROOT="/path/to/project" # Project root directory -export INPUT_DIR="/path/to/images" # Input images directory -export OUTPUT_DIR="/path/to/output" # Output directory +export AYLM_ROOT="/path/to/project" +export INPUT_DIR="/path/to/images" +export OUTPUT_DIR="/path/to/output" +export CUDA_VISIBLE_DEVICES="0" # GPU selection ``` -### Voxelizer Parameters +### 8.2 Voxelizer Parameters -| Parameter | Default | Description | -|-----------|---------|-------------| -| `voxel_size` | 0.005 | Voxel size in meters (5mm) | -| `statistical_nb_neighbors` | 20 | Neighbors for outlier removal | -| `ransac_distance_threshold` | 0.02 | RANSAC plane distance threshold | -| `ground_normal_threshold` | 0.8 | Ground normal Y-component threshold | +| Parameter | Default | Range | Description | +|-----------|---------|-------|-------------| +| `voxel_size` | 0.005 | 0.001-0.1 | Voxel resolution (meters) | +| `statistical_nb_neighbors` | 20 | 5-50 | Outlier removal neighbors | +| `ransac_distance_threshold` | 0.02 | 0.01-0.1 | RANSAC plane threshold | +| `ground_normal_threshold` | 0.8 | 0.5-1.0 | Ground normal Y-component | -### Pipeline Parameters +### 8.3 Safety Validation Parameters | Parameter | Default | Description | |-----------|---------|-------------| -| `DEFAULT_VOXEL_SIZE` | 0.05 | Navigation voxel size (5cm) | -| `DEFAULT_SLICE_RADIUS` | 20.0 | Point cloud slice radius (meters) | +| `DEFAULT_VOXEL_SIZE` | 0.05 | Safety validation voxel size (5cm) | +| `DEFAULT_SLICE_RADIUS` | 20.0 | Processing radius (meters) | | `DEFAULT_FOV_DEGREES` | 60.0 | Camera field of view | | `DEFAULT_DENSITY_THRESHOLD` | 3 | Minimum points per voxel | +| `TTC_WARNING_THRESHOLD` | 3.0 | Time-to-collision warning (seconds) | +| `TTC_CRITICAL_THRESHOLD` | 1.5 | Time-to-collision critical (seconds) | -### Video Config (YAML) +--- + +## 9. Project Structure -```yaml -# inputs/videos/video_config.yaml -frame_extraction_method: interval # interval/uniform/keyframe -frame_interval: 1.0 # seconds -gpu_acceleration: auto # auto/cuda/mps/none -output_format: png ``` +A.YLM/ +├── src/aylm/ # Core Package +│ ├── cli.py # Command Line Interface +│ └── tools/ # Processing Modules +│ ├── pointcloud_voxelizer.py # Occupancy 2.0 Voxelization +│ ├── pipeline_processor.py # Parallel Pipeline Orchestration +│ ├── video_pipeline.py # Video Sequence Processing +│ ├── semantic_fusion.py # 2D→3D Semantic Projection +│ ├── object_detector.py # YOLO Instance Segmentation +│ ├── obstacle_marker.py # DBSCAN Obstacle Clustering +│ ├── pointcloud_slicer.py # Spatial ROI Extraction +│ └── coordinate_utils.py # CV↔Robot Coordinate Transform +├── ml-sharp/ # Apple SHARP Model (submodule) +├── models/ # Model Checkpoints +├── inputs/ # Input Data +│ ├── input_images/ # RGB Images +│ └── videos/ # Video Files +├── outputs/ # Output Data +│ ├── output_gaussians/ # 3DGS Point Clouds +│ ├── voxelized/ # Occupancy Grids +│ ├── detections/ # Detection Results +│ └── safety_validation/ # Safety Validation Results +├── tests/ # Test Suite +├── run.sh # One-Click Execution Script +└── pyproject.toml # Project Configuration +``` + +--- + +## 10. Future Work + +### 10.1 Advanced Safety Learning +Implementation of reinforcement learning for adaptive safety boundary optimization. + +### 10.2 Multi-Sensor Fusion +Integration with LiDAR for enhanced geometric accuracy in safety-critical scenarios. + +### 10.3 Temporal Safety Reasoning +Incorporation of temporal models for predictive safety validation. -## Semantic Labels +### 10.4 Federated Safety Learning +Distributed learning across vehicle fleets for collective safety improvement. -| Label | Color | Description | -|-------|-------|-------------| -| GROUND | Brown | Ground plane (removed for navigation) | -| VEHICLE | Blue | Cars, trucks, buses | -| PEDESTRIAN | Red | People | -| BICYCLE | Cyan | Bicycles, motorcycles | -| BUILDING | Gray | Static structures | -| VEGETATION | Green | Trees, plants | -| UNKNOWN | White | Unclassified points | +--- -## Development +## 11. Development -### Running Tests +### 11.1 Testing ```bash -# Run all tests +# Run full test suite pytest tests/ -v -# Run specific test -pytest tests/unit/test_semantic_fusion.py -v - -# Run with coverage +# Run with coverage report pytest --cov=aylm --cov-report=html + +# Run specific module tests +pytest tests/unit/test_semantic_fusion.py -v ``` -### Code Quality +### 11.2 Code Quality ```bash # Format code @@ -306,53 +708,71 @@ ruff check src/aylm tests mypy src/aylm ``` -## Troubleshooting +--- -### Common Issues +## 12. Troubleshooting -1. **Open3D Installation Failed** - - Use Python 3.11 (Open3D may not support Python 3.13) - - The system will automatically fall back to numpy implementation +| Issue | Cause | Solution | +|-------|-------|----------| +| Open3D installation failed | Python 3.13 incompatibility | Use Python 3.11 | +| YOLO model not found | First-time download | Run `pip install ultralytics` | +| SHARP model download failed | Network issue | Manual download from Apple CDN | +| Out of memory | Large point cloud | Increase `voxel_size` or use `--slice-radius` | +| CUDA/MPS unavailable | Driver issue | Check `torch.cuda.is_available()` | -2. **YOLO Model Not Found** - ```bash - pip install ultralytics - # Model auto-downloads on first use (~6MB) - ``` +--- -3. **Model Download Failed** - - Check network connection - - Model URL: `https://ml-site.cdn-apple.com/models/sharp/sharp_2572gikvuh.pt` - - Model size: ~2.8GB +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. -4. **Out of Memory** - - Reduce input image resolution - - Increase voxel size (e.g., 0.01 for 1cm voxels) - - Use `--slice-radius` to limit processing area +## Acknowledgments -5. **CUDA/MPS Issues** - ```bash - # Check PyTorch device availability - python -c "import torch; print(torch.cuda.is_available(), torch.backends.mps.is_available())" - ``` +- **Apple Inc.** - SHARP Vision Transformer model and research +- **Ultralytics** - YOLO object detection framework +- **Open3D Community** - Point cloud processing library +- **PyTorch Team** - Deep learning framework -## License +## Citation -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +If you use A-YLM in your research, please cite: + +```bibtex +@software{aylm2026, + title={A-YLM: Geometric Constitutional AI for Embodied Intelligence}, + author={TRIP (appergb)}, + year={2026}, + url={https://github.com/appergb/A.YLM}, + note={Self-supervised safety framework extending Constitutional AI to physical world} +} +``` + +--- ## Acknowledgments -- **Apple Inc.** - SHARP model and research -- **Ultralytics** - YOLO object detection +- **Anthropic** - Constitutional AI paradigm that inspired this work +- **Apple Inc.** - SHARP Vision Transformer model and research +- **Ultralytics** - YOLO object detection framework - **Open3D Community** - Point cloud processing library - **PyTorch Team** - Deep learning framework -## Author +--- -**TRIP** (appergb) +## Contact & Community -**Contributors**: closer, true +- **Author**: TRIP (appergb) +- **Contributors**: Claude Code, Junie +- **Project Status**: Production Ready +- **Issues**: [GitHub Issues](https://github.com/appergb/A.YLM/issues) +- **Discussions**: [GitHub Discussions](https://github.com/appergb/A.YLM/discussions) --- -**Project Status**: Production Ready +
+ +**Star us on GitHub if you find this project useful!** + +*A-YLM: Teaching AI to understand the physical world through geometric constitution* + +
diff --git a/configs/constitution_example.yaml b/configs/constitution_example.yaml new file mode 100644 index 0000000..b2828fc --- /dev/null +++ b/configs/constitution_example.yaml @@ -0,0 +1,57 @@ +# A-YLM 宪法配置示例 +# 用户可以根据自己的需求修改此配置 + +# 宪法原则列表 +principles: + # 碰撞检测 - 最高优先级 + - name: no_collision + severity: critical + enabled: true + params: + check_trajectory: true + prediction_horizon: 2.0 # 预测时间范围(秒) + + # TTC 安全 - 高优先级 + - name: ttc_safety + severity: high + enabled: true + params: + warning_threshold: 3.0 # 警告阈值(秒) + critical_threshold: 1.5 # 关键阈值(秒) + + # 安全跟车距离 + - name: safe_following + severity: high + enabled: true + params: + time_gap: 2.0 # 时间间隔(秒) + + # 车道合规 + - name: lane_compliance + severity: medium + enabled: true + + # 速度限制 + - name: speed_limit + severity: medium + enabled: true + params: + max_speed: 120 # km/h + +# 安全阈值 +thresholds: + ttc_warning: 3.0 # TTC 警告阈值(秒) + ttc_critical: 1.5 # TTC 关键阈值(秒) + min_safe_distance: 2.0 # 最小安全距离(米) + speed_distance_factor: 0.5 # 速度-距离系数 + +# 打分权重 +weights: + collision: 1.0 # 碰撞权重 + ttc: 0.8 # TTC 权重 + boundary: 0.5 # 边界权重 + +# 训练信号配置 +training: + generate_positive_signals: false # 是否生成正样本 + export_format: json # 导出格式: json, tfrecord, parquet diff --git a/fuse.sh b/fuse.sh new file mode 100755 index 0000000..5ed387f --- /dev/null +++ b/fuse.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# 多帧点云融合脚本 +# 用法: ./fuse.sh [输入目录] [输出文件] + +set -e + +# 颜色定义 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 默认路径 +DEFAULT_INPUT="outputs/video_output/voxelized" +DEFAULT_OUTPUT="outputs/video_output/fused_map.ply" + +# 解析参数 +INPUT_DIR="${1:-$DEFAULT_INPUT}" +OUTPUT_FILE="${2:-$DEFAULT_OUTPUT}" + +# 显示帮助 +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "多帧点云融合脚本" + echo "" + echo "用法: ./fuse.sh [输入目录] [输出文件] [选项]" + echo "" + echo "参数:" + echo " 输入目录 包含 vox_*.ply 文件的目录 (默认: $DEFAULT_INPUT)" + echo " 输出文件 融合后的点云文件路径 (默认: $DEFAULT_OUTPUT)" + echo "" + echo "选项:" + echo " -h, --help 显示帮助" + echo "" + echo "示例:" + echo " ./fuse.sh # 使用默认路径" + echo " ./fuse.sh outputs/my_scan # 指定输入目录" + echo " ./fuse.sh outputs/my_scan my_map.ply # 指定输入和输出" + echo "" + echo "高级用法 (直接调用 aylm):" + echo " aylm fuse -i 输入目录 -o 输出文件 --icp-distance 0.1 --voxel-size 0.05" + exit 0 +fi + +# 检查输入目录 +if [[ ! -d "$INPUT_DIR" ]]; then + echo -e "${YELLOW}错误: 输入目录不存在: $INPUT_DIR${NC}" + exit 1 +fi + +# 统计点云文件 +PLY_COUNT=$(find "$INPUT_DIR" -name "vox_*.ply" 2>/dev/null | wc -l | tr -d ' ') + +if [[ "$PLY_COUNT" -eq 0 ]]; then + echo -e "${YELLOW}错误: 未找到 vox_*.ply 文件${NC}" + echo "目录: $INPUT_DIR" + exit 1 +fi + +# 显示信息 +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${GREEN}多帧点云融合${NC}" +echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" +echo -e "输入目录: ${GREEN}$INPUT_DIR${NC}" +echo -e "点云数量: ${GREEN}$PLY_COUNT${NC} 帧" +echo -e "输出文件: ${GREEN}$OUTPUT_FILE${NC}" +echo "" + +# 列出点云文件 +echo "点云文件:" +find "$INPUT_DIR" -name "vox_*.ply" | sort | while read f; do + SIZE=$(du -h "$f" | cut -f1) + echo " $(basename "$f") ($SIZE)" +done +echo "" + +# 激活虚拟环境 +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if [[ -d "$SCRIPT_DIR/aylm_env" ]]; then + source "$SCRIPT_DIR/aylm_env/bin/activate" +fi + +# 执行融合 +echo -e "${BLUE}开始融合...${NC}" +echo "" + +aylm fuse -i "$INPUT_DIR" -o "$OUTPUT_FILE" -v + +# 检查结果 +if [[ -f "$OUTPUT_FILE" ]]; then + echo "" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${GREEN}融合完成!${NC}" + echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + + OUTPUT_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1) + echo "" + echo -e "输出文件: ${GREEN}$OUTPUT_FILE${NC} ($OUTPUT_SIZE)" + + # 检查位姿文件 + POSES_FILE="${OUTPUT_FILE%.ply}.poses.json" + if [[ -f "$POSES_FILE" ]]; then + echo -e "位姿轨迹: ${GREEN}$POSES_FILE${NC}" + fi + + echo "" + echo "查看结果:" + echo " open $OUTPUT_FILE # macOS 默认应用打开" + echo " meshlab $OUTPUT_FILE # 使用 MeshLab 查看" +fi diff --git a/pyproject.toml b/pyproject.toml index 5540449..a03fb7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "aylm" version = "2.0.0" -description = "AYLM - Advanced YLM for 3D Gaussian Splatting" +description = "A-YLM: Geometric Constitutional AI for Embodied Intelligence - Bidirectional fusion with E2E driving systems (FSD/ADS), providing 3D point cloud input and safety supervision. Self-supervised learning without human annotation. Edge-deployable real-time geometric validation." readme = "README.md" license = {text = "MIT"} requires-python = ">=3.9" @@ -20,6 +20,8 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Image Recognition", + "Topic :: Scientific/Engineering :: Visualization", ] dependencies = [ "numpy", @@ -41,9 +43,11 @@ semantic = [ ] dev = [ "pytest", + "pytest-cov", "black", "isort", "ruff", + "mypy", ] all = [ "open3d>=0.18.0", @@ -68,3 +72,47 @@ line_length = 88 [tool.ruff] line-length = 88 target-version = "py39" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # Pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade + "SIM", # flake8-simplify + "TCH", # flake8-type-checking + "RUF", # Ruff-specific rules +] +ignore = [ + "E501", # line too long (handled by black) + "B008", # function call in default argument + "B905", # zip without strict + "RUF001", # ambiguous unicode in string (中文逗号等) + "RUF002", # ambiguous unicode in docstring + "RUF003", # ambiguous unicode in comment + "RUF022", # __all__ not sorted (按功能分组更清晰) + "TC002", # move import into TYPE_CHECKING (运行时需要) +] + +[tool.ruff.lint.isort] +known-first-party = ["aylm"] + +[tool.mypy] +python_version = "3.11" +warn_return_any = false +warn_unused_configs = true +disallow_untyped_defs = false +check_untyped_defs = false +ignore_missing_imports = true +exclude = [ + "tests/", + "ml-sharp/", + "aylm_env/", +] + +[[tool.mypy.overrides]] +module = "yaml" +ignore_missing_imports = true diff --git a/run.sh b/run.sh index 39217ba..3213f15 100755 --- a/run.sh +++ b/run.sh @@ -62,6 +62,10 @@ ${YELLOW}点云切片选项:${NC} --no-slice 禁用点云切片 --slice-radius 切片半径/米 (默认: 10.0) +${YELLOW}目标跟踪选项:${NC} + --track 启用目标跟踪 (默认) + --no-track 禁用目标跟踪 + ${YELLOW}示例:${NC} ./run.sh --setup # 初始化 ./run.sh -i ./images # 处理图像 @@ -158,9 +162,10 @@ run_auto() { main() { local action="auto" input_dir="inputs/input_images" output_dir="" config_file="" local extra_args=() check_only=false use_gpu=false frame_interval="" fps="10" loop=false - # 语义检测和切片参数(默认都启用) - local semantic=true semantic_model="yolo11n-seg.pt" semantic_confidence="0.5" + # 语义检测、切片和跟踪参数(默认都启用) + local semantic=true semantic_model="yolo11n-seg.pt" semantic_confidence="0.25" local slice=true slice_radius="10.0" + local track=true while [[ $# -gt 0 ]]; do case "$1" in @@ -185,6 +190,9 @@ main() { --slice) slice=true; shift ;; --no-slice) slice=false; shift ;; --slice-radius) slice_radius="$2"; shift 2 ;; + # 跟踪参数 + --track) track=true; shift ;; + --no-track) track=false; shift ;; *) extra_args+=("$1"); shift ;; esac done @@ -213,6 +221,13 @@ main() { slice_args+=("--no-slice") fi + local track_args=() + if [[ "$track" == true ]]; then + track_args+=("--track") + else + track_args+=("--no-track") + fi + case "$action" in setup) log_step "下载模型..."; python3 -m aylm.cli setup --download ;; voxelize) run_voxelize "${extra_args[@]}" ;; @@ -223,6 +238,7 @@ main() { local video_args=("${extra_args[@]}") [[ -n "$config_file" ]] && video_args+=("-c" "$config_file") [[ "$use_gpu" == true ]] && video_args+=("--use-gpu") + video_args+=("${semantic_args[@]}" "${slice_args[@]}" "${track_args[@]}") show_banner "视频处理" run_video_process "${video_args[@]}" ;; video-extract) @@ -234,7 +250,7 @@ main() { local play_args=("-i" "$input_dir" "--fps" "$fps") [[ "$loop" == true ]] && play_args+=("--loop") run_video_play "${play_args[@]}" ;; - auto) run_auto "$input_dir" "${extra_args[@]}" "${semantic_args[@]}" "${slice_args[@]}" ;; + auto) run_auto "$input_dir" "${extra_args[@]}" "${semantic_args[@]}" "${slice_args[@]}" "${track_args[@]}" ;; esac log_info "完成" diff --git a/src/aylm/cli.py b/src/aylm/cli.py index 60a2da1..49900c3 100644 --- a/src/aylm/cli.py +++ b/src/aylm/cli.py @@ -283,7 +283,18 @@ def cmd_video_process(args: argparse.Namespace) -> int: from aylm.tools.video_config import load_or_create_config from aylm.tools.video_types import FrameExtractionMethod, GPUAcceleration - config = load_or_create_config(Path(args.config) if args.config else None) + # 确定配置文件路径:优先使用命令行指定,否则查找视频目录下的配置 + config_path = None + if args.config: + config_path = Path(args.config) + else: + # 自动查找视频所在目录的配置文件 + video_dir_config = video_path.parent / "video_config.yaml" + if video_dir_config.exists(): + config_path = video_dir_config + print(f" 发现配置文件: {config_path}") + + config = load_or_create_config(config_path) if args.frame_interval: config.frame_interval = args.frame_interval config.frame_extraction_method = FrameExtractionMethod.INTERVAL @@ -317,6 +328,14 @@ def cmd_video_process(args: argparse.Namespace) -> int: # 解析语义检测选项 enable_semantic = args.semantic and not getattr(args, "no_semantic", False) + # 解析切片选项 + enable_slice = getattr(args, "slice", True) and not getattr( + args, "no_slice", False + ) + # 解析跟踪选项 + enable_tracking = getattr(args, "track", True) and not getattr( + args, "no_track", False + ) pipeline_config = VideoPipelineConfig( video_config=config, @@ -326,6 +345,9 @@ def cmd_video_process(args: argparse.Namespace) -> int: enable_semantic=enable_semantic, semantic_model=args.semantic_model, semantic_confidence=args.semantic_confidence, + enable_slice=enable_slice, + slice_radius=getattr(args, "slice_radius", 10.0), + enable_tracking=enable_tracking, ) stats = VideoPipelineProcessor(pipeline_config).process(video_path, output_dir) @@ -370,7 +392,18 @@ def cmd_video_extract(args: argparse.Namespace) -> int: from aylm.tools.video_config import load_or_create_config from aylm.tools.video_types import FrameExtractionMethod - config = load_or_create_config(Path(args.config) if args.config else None) + # 确定配置文件路径:优先使用命令行指定,否则查找视频目录下的配置 + config_path = None + if args.config: + config_path = Path(args.config) + else: + # 自动查找视频所在目录的配置文件 + video_dir_config = video_path.parent / "video_config.yaml" + if video_dir_config.exists(): + config_path = video_dir_config + print(f" 发现配置文件: {config_path}") + + config = load_or_create_config(config_path) if args.frame_interval: config.frame_interval = args.frame_interval config.frame_extraction_method = FrameExtractionMethod.INTERVAL @@ -538,6 +571,62 @@ def cmd_pipeline(args: argparse.Namespace) -> int: return 1 +def cmd_fuse(args: argparse.Namespace) -> int: + """多帧点云配准融合.""" + input_dir = Path(args.input) + if not input_dir.exists(): + logger.error(f"输入目录不存在: {input_dir}") + return 1 + + output_path = Path(args.output) if args.output else input_dir / "fused_map.ply" + + if args.verbose: + logging.getLogger("aylm").setLevel(logging.DEBUG) + + print(f"AYLM v{get_version()} - 多帧点云融合\n") + print(f"[输入] {input_dir}") + print(f"[模式] {args.pattern}") + print("[配置]") + print(f" ICP距离: {args.icp_distance}m") + print(f" 体素大小: {args.voxel_size}m") + print(f"[输出] {output_path}") + print("=" * 50) + + try: + from aylm.tools.multiframe_fusion import MultiframeFusion, RegistrationConfig + + config = RegistrationConfig( + icp_max_correspondence_distance=args.icp_distance, + fusion_voxel_size=args.voxel_size, + ) + + fusion = MultiframeFusion(config) + result = fusion.fuse_from_directory(input_dir, args.pattern) + + if len(result.fused_pointcloud.points) == 0: + logger.error("融合失败,未生成有效点云") + return 1 + + fusion.save_fused_map(result, output_path) + + print("\n" + "=" * 50) + print("[完成] 多帧融合结束") + print(f" 总帧数: {result.total_frames}") + print(f" 成功配准: {result.successful_registrations}") + print(f" 融合点数: {len(result.fused_pointcloud.points)}") + print(f" 耗时: {result.fusion_time:.2f}s") + + return 0 + + except ImportError as e: + logger.error(f"导入融合模块失败: {e}") + logger.error("请确保已安装 Open3D: pip install open3d") + return 1 + except Exception as e: + logger.error(f"融合失败: {e}") + return 1 + + def create_parser() -> argparse.ArgumentParser: """创建命令行解析器.""" parser = argparse.ArgumentParser( @@ -585,7 +674,7 @@ def create_parser() -> argparse.ArgumentParser: ) p.add_argument("--no-semantic", action="store_true", help="禁用语义检测") p.add_argument("--semantic-model", default="yolo11n-seg.pt", help="YOLO模型名称") - p.add_argument("--semantic-confidence", type=float, default=0.5, help="检测置信度") + p.add_argument("--semantic-confidence", type=float, default=0.25, help="检测置信度") # 切片选项 p.add_argument("--slice", action="store_true", default=True, help="启用点云切片") p.add_argument("--no-slice", action="store_true", help="禁用点云切片") @@ -618,7 +707,14 @@ def create_parser() -> argparse.ArgumentParser: p.add_argument("--semantic", action="store_true", default=True, help="启用语义检测") p.add_argument("--no-semantic", action="store_true", help="禁用语义检测") p.add_argument("--semantic-model", default="yolo11n-seg.pt", help="YOLO模型名称") - p.add_argument("--semantic-confidence", type=float, default=0.5, help="检测置信度") + p.add_argument("--semantic-confidence", type=float, default=0.25, help="检测置信度") + # 点云切片选项 + p.add_argument("--slice", action="store_true", default=True, help="启用点云切片") + p.add_argument("--no-slice", action="store_true", help="禁用点云切片") + p.add_argument("--slice-radius", type=float, default=10.0, help="切片半径(米)") + # 目标跟踪选项 + p.add_argument("--track", action="store_true", default=True, help="启用目标跟踪") + p.add_argument("--no-track", action="store_true", help="禁用目标跟踪") p.add_argument("-v", "--verbose", action="store_true", help="详细输出") p.set_defaults(func=cmd_video_process) @@ -639,6 +735,18 @@ def create_parser() -> argparse.ArgumentParser: p.add_argument("-v", "--verbose", action="store_true", help="详细输出") p.set_defaults(func=cmd_video_play) + # fuse - 多帧点云融合 + p = subs.add_parser("fuse", help="多帧点云配准融合") + p.add_argument("-i", "--input", required=True, help="输入点云目录") + p.add_argument("-o", "--output", help="输出融合地图路径") + p.add_argument("--pattern", default="vox_*.ply", help="文件匹配模式") + p.add_argument( + "--icp-distance", type=float, default=0.05, help="ICP最大对应距离(米)" + ) + p.add_argument("--voxel-size", type=float, default=0.02, help="融合后体素大小(米)") + p.add_argument("-v", "--verbose", action="store_true", help="详细输出") + p.set_defaults(func=cmd_fuse) + return parser diff --git a/src/aylm/constitution/__init__.py b/src/aylm/constitution/__init__.py new file mode 100644 index 0000000..91c0851 --- /dev/null +++ b/src/aylm/constitution/__init__.py @@ -0,0 +1,38 @@ +"""A-YLM 几何宪法式 AI 核心模块。 + +本模块提供开放式接口,允许用户自定义: +- 宪法原则(ConstitutionPrinciple) +- 安全打分器(SafetyScorer) +- 训练信号生成器(TrainingSignalGenerator) + +设计理念: +- 我们提供基础设施和接口定义 +- 具体的打分逻辑和训练方式由用户根据需求实现 +- 支持插件机制,便于第三方扩展 +""" + +from .base import ( + ConstitutionPrinciple, + Severity, + ViolationResult, +) +from .config import ConstitutionConfig +from .registry import ConstitutionRegistry +from .scorer import SafetyScore, SafetyScorer +from .training import TrainingSignal, TrainingSignalGenerator + +__all__ = [ + # 基础类型 + "Severity", + "ViolationResult", + # 抽象基类 + "ConstitutionPrinciple", + "SafetyScorer", + "TrainingSignalGenerator", + # 数据类 + "SafetyScore", + "TrainingSignal", + "ConstitutionConfig", + # 注册机制 + "ConstitutionRegistry", +] diff --git a/src/aylm/constitution/base.py b/src/aylm/constitution/base.py new file mode 100644 index 0000000..392b489 --- /dev/null +++ b/src/aylm/constitution/base.py @@ -0,0 +1,133 @@ +"""宪法原则基类定义。 + +本模块定义了几何宪法式 AI 的核心抽象: +- Severity: 违规严重性等级 +- ViolationResult: 违规检测结果 +- ConstitutionPrinciple: 宪法原则基类 + +用户可以继承 ConstitutionPrinciple 实现自定义的安全规则。 +""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from enum import Enum +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from .types import AIDecision, SceneState + + +class Severity(Enum): + """违规严重性等级。 + + 用于定义宪法原则被违反时的严重程度, + 影响最终安全分数的计算和推荐动作。 + """ + + CRITICAL = 1.0 # 关键:必须立即阻止(如即将碰撞) + HIGH = 0.8 # 高:强烈警告,建议干预 + MEDIUM = 0.5 # 中:一般警告,需要注意 + LOW = 0.2 # 低:轻微提示,可忽略 + + +@dataclass +class ViolationResult: + """违规检测结果。 + + Attributes: + violated: 是否违反原则 + severity: 违规严重性 + confidence: 检测置信度 (0.0-1.0) + description: 违规描述(人类可读) + metrics: 相关度量值(如距离、TTC 等) + correction_hint: 纠正建议(可选,用于生成训练信号) + """ + + violated: bool + severity: Severity + confidence: float + description: str + metrics: dict[str, float] = field(default_factory=dict) + correction_hint: dict[str, Any] | None = None + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "violated": self.violated, + "severity": self.severity.name, + "severity_weight": self.severity.value, + "confidence": self.confidence, + "description": self.description, + "metrics": self.metrics, + "correction_hint": self.correction_hint, + } + + +class ConstitutionPrinciple(ABC): + """宪法原则基类。 + + 所有安全规则都应继承此类并实现 evaluate 方法。 + 这是 A-YLM 几何宪法式 AI 的核心抽象。 + + Example: + >>> class NoCollisionPrinciple(ConstitutionPrinciple): + ... @property + ... def name(self) -> str: + ... return "no_collision" + ... + ... @property + ... def severity(self) -> Severity: + ... return Severity.CRITICAL + ... + ... def evaluate(self, state, decision) -> ViolationResult: + ... # 实现碰撞检测逻辑 + ... collision = check_collision(state.obstacles, decision.trajectory) + ... return ViolationResult( + ... violated=collision, + ... severity=self.severity, + ... confidence=0.95, + ... description="检测到碰撞风险" if collision else "安全", + ... ) + """ + + @property + @abstractmethod + def name(self) -> str: + """原则名称(唯一标识符)。""" + pass + + @property + @abstractmethod + def severity(self) -> Severity: + """默认严重性等级。""" + pass + + @property + def description(self) -> str: + """原则描述(可选覆盖)。""" + return f"宪法原则: {self.name}" + + @property + def enabled(self) -> bool: + """是否启用(可动态控制)。""" + return True + + @abstractmethod + def evaluate( + self, + state: "SceneState", + decision: "AIDecision", + ) -> ViolationResult: + """评估 AI 决策是否违反此原则。 + + Args: + state: 当前场景状态(包含障碍物、自车状态等) + decision: AI 的决策(包含规划轨迹、控制指令等) + + Returns: + ViolationResult: 违规检测结果 + """ + pass + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(name={self.name}, severity={self.severity.name})" diff --git a/src/aylm/constitution/config.py b/src/aylm/constitution/config.py new file mode 100644 index 0000000..2974050 --- /dev/null +++ b/src/aylm/constitution/config.py @@ -0,0 +1,188 @@ +"""宪法配置模块。 + +支持从 YAML/JSON 文件加载宪法配置,便于用户自定义规则。 +""" + +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + + +@dataclass +class PrincipleConfig: + """单个宪法原则配置。""" + + name: str + severity: str = "medium" # critical, high, medium, low + enabled: bool = True + params: dict[str, Any] = field(default_factory=dict) + + +@dataclass +class ConstitutionConfig: + """宪法配置。 + + 支持从 YAML/JSON 文件加载,便于用户自定义安全规则。 + + Example YAML: + ```yaml + principles: + - name: no_collision + severity: critical + enabled: true + - name: ttc_safety + severity: high + params: + warning_threshold: 3.0 + critical_threshold: 1.5 + + thresholds: + min_safe_distance: 2.0 + speed_distance_factor: 0.5 + + training: + generate_positive_signals: false + export_format: json + ``` + """ + + # 宪法原则列表 + principles: list[PrincipleConfig] = field(default_factory=list) + + # 安全阈值 + ttc_warning_threshold: float = 3.0 # TTC 警告阈值(秒) + ttc_critical_threshold: float = 1.5 # TTC 关键阈值(秒) + min_safe_distance: float = 2.0 # 最小安全距离(米) + speed_distance_factor: float = 0.5 # 速度-距离系数 + + # 训练信号配置 + generate_positive_signals: bool = False # 是否生成正样本 + signal_export_format: str = "json" # 导出格式 + + # 打分权重 + collision_weight: float = 1.0 + ttc_weight: float = 0.8 + boundary_weight: float = 0.5 + + def __post_init__(self): + """初始化默认原则(如果未指定)。""" + if not self.principles: + self.principles = self._default_principles() + + @staticmethod + def _default_principles() -> list[PrincipleConfig]: + """默认宪法原则。""" + return [ + PrincipleConfig(name="no_collision", severity="critical"), + PrincipleConfig(name="safe_following", severity="high"), + PrincipleConfig(name="ttc_safety", severity="high"), + PrincipleConfig(name="lane_compliance", severity="medium"), + PrincipleConfig(name="speed_limit", severity="medium"), + ] + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "ConstitutionConfig": + """从字典创建配置。""" + principles = [] + for p in data.get("principles", []): + principles.append( + PrincipleConfig( + name=p["name"], + severity=p.get("severity", "medium"), + enabled=p.get("enabled", True), + params=p.get("params", {}), + ) + ) + + thresholds = data.get("thresholds", {}) + training = data.get("training", {}) + weights = data.get("weights", {}) + + return cls( + principles=principles if principles else None, + ttc_warning_threshold=thresholds.get("ttc_warning", 3.0), + ttc_critical_threshold=thresholds.get("ttc_critical", 1.5), + min_safe_distance=thresholds.get("min_safe_distance", 2.0), + speed_distance_factor=thresholds.get("speed_distance_factor", 0.5), + generate_positive_signals=training.get("generate_positive_signals", False), + signal_export_format=training.get("export_format", "json"), + collision_weight=weights.get("collision", 1.0), + ttc_weight=weights.get("ttc", 0.8), + boundary_weight=weights.get("boundary", 0.5), + ) + + @classmethod + def from_json(cls, path: str | Path) -> "ConstitutionConfig": + """从 JSON 文件加载配置。""" + with open(path, encoding="utf-8") as f: + data = json.load(f) + return cls.from_dict(data) + + @classmethod + def from_yaml(cls, path: str | Path) -> "ConstitutionConfig": + """从 YAML 文件加载配置。""" + try: + import yaml + except ImportError as e: + raise ImportError("需要安装 pyyaml: pip install pyyaml") from e + + with open(path, encoding="utf-8") as f: + data = yaml.safe_load(f) + return cls.from_dict(data) + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "principles": [ + { + "name": p.name, + "severity": p.severity, + "enabled": p.enabled, + "params": p.params, + } + for p in self.principles + ], + "thresholds": { + "ttc_warning": self.ttc_warning_threshold, + "ttc_critical": self.ttc_critical_threshold, + "min_safe_distance": self.min_safe_distance, + "speed_distance_factor": self.speed_distance_factor, + }, + "training": { + "generate_positive_signals": self.generate_positive_signals, + "export_format": self.signal_export_format, + }, + "weights": { + "collision": self.collision_weight, + "ttc": self.ttc_weight, + "boundary": self.boundary_weight, + }, + } + + def save_json(self, path: str | Path) -> None: + """保存为 JSON 文件。""" + with open(path, "w", encoding="utf-8") as f: + json.dump(self.to_dict(), f, indent=2, ensure_ascii=False) + + def save_yaml(self, path: str | Path) -> None: + """保存为 YAML 文件。""" + try: + import yaml + except ImportError as e: + raise ImportError("需要安装 pyyaml: pip install pyyaml") from e + + with open(path, "w", encoding="utf-8") as f: + yaml.dump(self.to_dict(), f, allow_unicode=True, default_flow_style=False) + + def get_principle(self, name: str) -> PrincipleConfig | None: + """获取指定名称的原则配置。""" + for p in self.principles: + if p.name == name: + return p + return None + + def is_principle_enabled(self, name: str) -> bool: + """检查原则是否启用。""" + p = self.get_principle(name) + return p.enabled if p else False diff --git a/src/aylm/constitution/principles/__init__.py b/src/aylm/constitution/principles/__init__.py new file mode 100644 index 0000000..3640bf4 --- /dev/null +++ b/src/aylm/constitution/principles/__init__.py @@ -0,0 +1,12 @@ +"""内置宪法原则实现。 + +提供常用的宪法原则参考实现,用户可以直接使用或作为自定义实现的参考。 +""" + +from .collision import NoCollisionPrinciple +from .ttc import TTCSafetyPrinciple + +__all__ = [ + "NoCollisionPrinciple", + "TTCSafetyPrinciple", +] diff --git a/src/aylm/constitution/principles/collision.py b/src/aylm/constitution/principles/collision.py new file mode 100644 index 0000000..736cef4 --- /dev/null +++ b/src/aylm/constitution/principles/collision.py @@ -0,0 +1,139 @@ +"""碰撞检测宪法原则。 + +提供碰撞检测的参考实现,用户可以继承或修改。 +""" + +from typing import TYPE_CHECKING + +import numpy as np + +from ..base import ConstitutionPrinciple, Severity, ViolationResult +from ..registry import ConstitutionRegistry + +if TYPE_CHECKING: + from ..types import AIDecision, SceneState + + +@ConstitutionRegistry.register_principle("no_collision") +class NoCollisionPrinciple(ConstitutionPrinciple): + """无碰撞原则。 + + 检测 AI 决策轨迹是否与障碍物发生碰撞。 + + 数学表述: + ∀t: V_ego(t) ∩ V_obstacle(t) = ∅ + + 参数: + safety_margin: 安全边距(米) + prediction_horizon: 预测时间范围(秒) + """ + + def __init__( + self, + safety_margin: float = 0.5, + prediction_horizon: float = 2.0, + ): + self.safety_margin = safety_margin + self.prediction_horizon = prediction_horizon + + @property + def name(self) -> str: + return "no_collision" + + @property + def severity(self) -> Severity: + return Severity.CRITICAL + + @property + def description(self) -> str: + return "检测 AI 决策是否会导致碰撞" + + def evaluate( + self, + state: "SceneState", + decision: "AIDecision", + ) -> ViolationResult: + """评估碰撞风险。 + + Args: + state: 场景状态(包含障碍物列表) + decision: AI 决策(包含规划轨迹) + + Returns: + ViolationResult: 违规检测结果 + """ + if not state.obstacles: + return ViolationResult( + violated=False, + severity=self.severity, + confidence=1.0, + description="无障碍物", + ) + + if not decision.trajectory: + return ViolationResult( + violated=False, + severity=self.severity, + confidence=0.5, + description="无轨迹信息,无法评估", + ) + + # 检测轨迹与障碍物的最小距离 + min_distance = float("inf") + collision_obstacle = None + + for traj_point in decision.trajectory: + if traj_point.timestamp > self.prediction_horizon: + break + + for obstacle in state.obstacles: + # 获取障碍物中心(支持不同格式) + if hasattr(obstacle, "center_robot"): + obs_center = np.array(obstacle.center_robot) + elif hasattr(obstacle, "center"): + obs_center = np.array(obstacle.center) + else: + continue + + # 计算距离 + distance = np.linalg.norm(traj_point.position - obs_center) + + # 考虑障碍物尺寸 + if hasattr(obstacle, "dimensions"): + # 简化:使用最大维度作为半径 + obs_radius = max(obstacle.dimensions) / 2 + distance -= obs_radius + + if distance < min_distance: + min_distance = distance + collision_obstacle = obstacle + + # 判断是否碰撞 + collision = min_distance < self.safety_margin + + return ViolationResult( + violated=collision, + severity=self.severity, + confidence=0.9 if collision else 0.95, + description=( + f"检测到碰撞风险,最小距离 {min_distance:.2f}m" + if collision + else f"安全,最小距离 {min_distance:.2f}m" + ), + metrics={ + "min_distance": float(min_distance), + "safety_margin": self.safety_margin, + }, + correction_hint=( + { + "action": "avoid", + "obstacle_position": ( + collision_obstacle.center_robot + if hasattr(collision_obstacle, "center_robot") + else None + ), + } + if collision and collision_obstacle + else None + ), + ) diff --git a/src/aylm/constitution/principles/ttc.py b/src/aylm/constitution/principles/ttc.py new file mode 100644 index 0000000..8f871e7 --- /dev/null +++ b/src/aylm/constitution/principles/ttc.py @@ -0,0 +1,151 @@ +"""TTC(碰撞时间)安全原则。 + +提供 TTC 计算的参考实现。 +""" + +from typing import TYPE_CHECKING + +import numpy as np + +from ..base import ConstitutionPrinciple, Severity, ViolationResult +from ..registry import ConstitutionRegistry + +if TYPE_CHECKING: + from ..types import AIDecision, SceneState + + +@ConstitutionRegistry.register_principle("ttc_safety") +class TTCSafetyPrinciple(ConstitutionPrinciple): + """TTC 安全原则。 + + 计算与动态障碍物的碰撞时间(Time To Collision)。 + + 数学表述: + ∀o ∈ O_dynamic: TTC(ego, o) > τ_min + + 其中: + TTC = (d - d_safe) / v_rel + + 参数: + warning_threshold: 警告阈值(秒) + critical_threshold: 关键阈值(秒) + min_safe_distance: 最小安全距离(米) + """ + + def __init__( + self, + warning_threshold: float = 3.0, + critical_threshold: float = 1.5, + min_safe_distance: float = 2.0, + ): + self.warning_threshold = warning_threshold + self.critical_threshold = critical_threshold + self.min_safe_distance = min_safe_distance + + @property + def name(self) -> str: + return "ttc_safety" + + @property + def severity(self) -> Severity: + return Severity.HIGH + + @property + def description(self) -> str: + return "检测与动态障碍物的碰撞时间是否安全" + + def evaluate( + self, + state: "SceneState", + decision: "AIDecision", + ) -> ViolationResult: + """评估 TTC 安全性。 + + Args: + state: 场景状态 + decision: AI 决策 + + Returns: + ViolationResult: 违规检测结果 + """ + if not state.obstacles: + return ViolationResult( + violated=False, + severity=self.severity, + confidence=1.0, + description="无障碍物", + ) + + min_ttc = float("inf") + + ego_pos = state.ego_state.position + ego_vel = state.ego_state.velocity + + for obstacle in state.obstacles: + # 只检测动态障碍物 + if hasattr(obstacle, "motion") and obstacle.motion: + motion = obstacle.motion + if motion.is_stationary: + continue + + # 获取障碍物位置和速度 + if hasattr(obstacle, "center_robot"): + obs_pos = np.array(obstacle.center_robot) + else: + continue + + obs_vel = np.array(motion.velocity_robot) + + # 计算相对位置和速度 + rel_pos = obs_pos - ego_pos + rel_vel = obs_vel - ego_vel + + # 计算距离 + distance = np.linalg.norm(rel_pos) + + # 计算相对速度在连线方向的分量 + if distance > 0: + direction = rel_pos / distance + closing_speed = -np.dot(rel_vel, direction) + + # 只有在接近时才计算 TTC + if closing_speed > 0.1: # 接近速度阈值 + ttc = (distance - self.min_safe_distance) / closing_speed + + if ttc < min_ttc: + min_ttc = ttc + _ = obstacle # 保留用于未来扩展 + + # 判断违规 + if min_ttc < self.critical_threshold: + violated = True + severity = Severity.CRITICAL + desc = f"TTC 关键警告: {min_ttc:.1f}s < {self.critical_threshold}s" + elif min_ttc < self.warning_threshold: + violated = True + severity = Severity.HIGH + desc = f"TTC 警告: {min_ttc:.1f}s < {self.warning_threshold}s" + else: + violated = False + severity = self.severity + desc = f"TTC 安全: {min_ttc:.1f}s" + + return ViolationResult( + violated=violated, + severity=severity, + confidence=0.85, + description=desc, + metrics={ + "min_ttc": float(min_ttc) if min_ttc != float("inf") else -1, + "warning_threshold": self.warning_threshold, + "critical_threshold": self.critical_threshold, + }, + correction_hint=( + { + "action": "slow_down", + "target_ttc": self.warning_threshold, + } + if violated + else None + ), + ) diff --git a/src/aylm/constitution/registry.py b/src/aylm/constitution/registry.py new file mode 100644 index 0000000..2c45f2f --- /dev/null +++ b/src/aylm/constitution/registry.py @@ -0,0 +1,165 @@ +"""宪法原则注册表。 + +提供插件机制,允许第三方注册自定义的宪法原则和打分器。 +""" + +from typing import TYPE_CHECKING, ClassVar + +if TYPE_CHECKING: + from .base import ConstitutionPrinciple + from .scorer import SafetyScorer + from .training import TrainingSignalGenerator + + +class ConstitutionRegistry: + """宪法原则注册表。 + + 支持第三方插件注册自定义的宪法原则、打分器和训练信号生成器。 + + Example: + >>> from aylm.constitution import ConstitutionRegistry, ConstitutionPrinciple + >>> + >>> @ConstitutionRegistry.register_principle("my_custom_rule") + ... class MyCustomPrinciple(ConstitutionPrinciple): + ... @property + ... def name(self) -> str: + ... return "my_custom_rule" + ... # ... 实现其他方法 + >>> + >>> # 获取已注册的原则 + >>> principle_cls = ConstitutionRegistry.get_principle("my_custom_rule") + >>> principle = principle_cls() + """ + + _principles: ClassVar[dict[str, type["ConstitutionPrinciple"]]] = {} + _scorers: ClassVar[dict[str, type["SafetyScorer"]]] = {} + _generators: ClassVar[dict[str, type["TrainingSignalGenerator"]]] = {} + + @classmethod + def register_principle(cls, name: str): + """装饰器:注册自定义宪法原则。 + + Args: + name: 原则名称(唯一标识符) + + Example: + >>> @ConstitutionRegistry.register_principle("no_collision") + ... class NoCollisionPrinciple(ConstitutionPrinciple): + ... pass + """ + + def decorator(principle_cls: type["ConstitutionPrinciple"]): + cls._principles[name] = principle_cls + return principle_cls + + return decorator + + @classmethod + def register_scorer(cls, name: str): + """装饰器:注册自定义打分器。 + + Args: + name: 打分器名称 + + Example: + >>> @ConstitutionRegistry.register_scorer("weighted") + ... class WeightedScorer(SafetyScorer): + ... pass + """ + + def decorator(scorer_cls: type["SafetyScorer"]): + cls._scorers[name] = scorer_cls + return scorer_cls + + return decorator + + @classmethod + def register_generator(cls, name: str): + """装饰器:注册自定义训练信号生成器。 + + Args: + name: 生成器名称 + + Example: + >>> @ConstitutionRegistry.register_generator("tfrecord") + ... class TFRecordGenerator(TrainingSignalGenerator): + ... pass + """ + + def decorator(generator_cls: type["TrainingSignalGenerator"]): + cls._generators[name] = generator_cls + return generator_cls + + return decorator + + @classmethod + def get_principle(cls, name: str) -> type["ConstitutionPrinciple"] | None: + """获取已注册的原则类。""" + return cls._principles.get(name) + + @classmethod + def get_scorer(cls, name: str) -> type["SafetyScorer"] | None: + """获取已注册的打分器类。""" + return cls._scorers.get(name) + + @classmethod + def get_generator(cls, name: str) -> type["TrainingSignalGenerator"] | None: + """获取已注册的生成器类。""" + return cls._generators.get(name) + + @classmethod + def list_principles(cls) -> list[str]: + """列出所有已注册的原则名称。""" + return list(cls._principles.keys()) + + @classmethod + def list_scorers(cls) -> list[str]: + """列出所有已注册的打分器名称。""" + return list(cls._scorers.keys()) + + @classmethod + def list_generators(cls) -> list[str]: + """列出所有已注册的生成器名称。""" + return list(cls._generators.keys()) + + @classmethod + def create_principle(cls, name: str, **kwargs) -> "ConstitutionPrinciple": + """创建原则实例。 + + Args: + name: 原则名称 + **kwargs: 传递给原则构造函数的参数 + + Returns: + 原则实例 + + Raises: + KeyError: 如果原则未注册 + """ + principle_cls = cls._principles.get(name) + if principle_cls is None: + raise KeyError(f"原则 '{name}' 未注册。已注册: {cls.list_principles()}") + return principle_cls(**kwargs) + + @classmethod + def create_scorer(cls, name: str, **kwargs) -> "SafetyScorer": + """创建打分器实例。""" + scorer_cls = cls._scorers.get(name) + if scorer_cls is None: + raise KeyError(f"打分器 '{name}' 未注册。已注册: {cls.list_scorers()}") + return scorer_cls(**kwargs) + + @classmethod + def create_generator(cls, name: str, **kwargs) -> "TrainingSignalGenerator": + """创建生成器实例。""" + generator_cls = cls._generators.get(name) + if generator_cls is None: + raise KeyError(f"生成器 '{name}' 未注册。已注册: {cls.list_generators()}") + return generator_cls(**kwargs) + + @classmethod + def clear(cls) -> None: + """清除所有注册(主要用于测试)。""" + cls._principles.clear() + cls._scorers.clear() + cls._generators.clear() diff --git a/src/aylm/constitution/scorer.py b/src/aylm/constitution/scorer.py new file mode 100644 index 0000000..453116e --- /dev/null +++ b/src/aylm/constitution/scorer.py @@ -0,0 +1,137 @@ +"""安全打分器模块。 + +本模块定义了安全打分的抽象接口: +- SafetyScore: 安全评分结果 +- SafetyScorer: 安全打分器基类 + +用户可以继承 SafetyScorer 实现自定义的打分逻辑。 +""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from enum import Enum +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from ..tools.obstacle_marker import ObstacleBox3D + from .types import AIDecision, EgoState + + +class RecommendedAction(Enum): + """推荐动作。""" + + SAFE = "safe" # 安全,无需干预 + CAUTION = "caution" # 注意,轻微风险 + WARNING = "warning" # 警告,需要关注 + INTERVENTION = "intervention" # 干预,建议接管 + EMERGENCY_STOP = "emergency_stop" # 紧急停车 + + +@dataclass +class SafetyScore: + """安全评分结果。 + + Attributes: + overall: 综合安全分数 (0.0-1.0,1.0 最安全) + collision_score: 碰撞风险分数 + ttc_score: TTC(碰撞时间)分数 + boundary_score: 边界合规分数 + violations: 违规的原则名称列表 + violation_details: 违规详情 + recommended_action: 推荐动作 + confidence: 评分置信度 + """ + + overall: float + collision_score: float = 1.0 + ttc_score: float = 1.0 + boundary_score: float = 1.0 + violations: list[str] = field(default_factory=list) + violation_details: list[dict[str, Any]] = field(default_factory=list) + recommended_action: RecommendedAction = RecommendedAction.SAFE + confidence: float = 1.0 + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "overall": self.overall, + "scores": { + "collision": self.collision_score, + "ttc": self.ttc_score, + "boundary": self.boundary_score, + }, + "violations": self.violations, + "violation_details": self.violation_details, + "recommended_action": self.recommended_action.value, + "confidence": self.confidence, + } + + @property + def is_safe(self) -> bool: + """是否安全(无违规)。""" + return len(self.violations) == 0 + + @property + def needs_intervention(self) -> bool: + """是否需要干预。""" + return self.recommended_action in ( + RecommendedAction.INTERVENTION, + RecommendedAction.EMERGENCY_STOP, + ) + + +class SafetyScorer(ABC): + """安全打分器基类。 + + 用户可以继承此类实现自定义的打分逻辑。 + 不同的 AI 系统可能有不同的安全标准和打分方式。 + + Example: + >>> class MySafetyScorer(SafetyScorer): + ... def score(self, obstacles, ego_state, ai_decision) -> SafetyScore: + ... # 实现自定义打分逻辑 + ... collision_risk = self._check_collision(obstacles, ai_decision) + ... ttc = self._compute_ttc(obstacles, ego_state) + ... return SafetyScore( + ... overall=min(collision_risk, ttc), + ... collision_score=collision_risk, + ... ttc_score=ttc, + ... ) + """ + + @abstractmethod + def score( + self, + obstacles: list["ObstacleBox3D"], + ego_state: "EgoState", + ai_decision: "AIDecision", + ) -> SafetyScore: + """计算安全分数。 + + Args: + obstacles: 3D 障碍物列表(来自 A-YLM 感知模块) + ego_state: 自车状态(位置、速度、航向等) + ai_decision: AI 的决策(规划轨迹、控制指令等) + + Returns: + SafetyScore: 安全评分结果 + """ + pass + + def score_from_scene( + self, + scene_data: dict[str, Any], + ) -> SafetyScore: + """从场景数据计算安全分数(便捷方法)。 + + Args: + scene_data: 场景数据字典,包含 obstacles、ego_state、ai_decision + + Returns: + SafetyScore: 安全评分结果 + """ + return self.score( + obstacles=scene_data.get("obstacles", []), + ego_state=scene_data.get("ego_state"), + ai_decision=scene_data.get("ai_decision"), + ) diff --git a/src/aylm/constitution/training.py b/src/aylm/constitution/training.py new file mode 100644 index 0000000..57b3508 --- /dev/null +++ b/src/aylm/constitution/training.py @@ -0,0 +1,175 @@ +"""训练信号生成模块。 + +本模块定义了自监督学习的训练信号接口: +- TrainingSignal: 训练信号数据结构 +- TrainingSignalGenerator: 训练信号生成器基类 + +这是实现"几何宪法式 AI 自循环自训练"的关键模块。 +用户可以根据自己的 AI 训练框架实现具体的信号生成逻辑。 +""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from enum import Enum +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from .scorer import SafetyScore + from .types import AIDecision, SceneState + + +class SignalType(Enum): + """训练信号类型。""" + + POSITIVE = "positive" # 正样本:安全决策 + NEGATIVE = "negative" # 负样本:违规决策 + CORRECTION = "correction" # 纠正样本:包含正确行为指导 + + +@dataclass +class TrainingSignal: + """训练信号。 + + 用于自监督学习的训练数据,可上传到云端用于 AI 模型改进。 + + Attributes: + timestamp: 时间戳 + frame_id: 帧 ID + signal_type: 信号类型(正/负/纠正) + safety_score: 安全分数 + violations: 违规的原则列表 + scene_context: 场景上下文(障碍物、自车状态等) + ai_decision: AI 的原始决策 + correction_target: 纠正目标(如果是纠正样本) + metadata: 额外元数据 + """ + + timestamp: float + frame_id: int + signal_type: SignalType + safety_score: float + violations: list[str] = field(default_factory=list) + scene_context: dict[str, Any] = field(default_factory=dict) + ai_decision: dict[str, Any] = field(default_factory=dict) + correction_target: dict[str, Any] | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式(用于序列化)。""" + return { + "timestamp": self.timestamp, + "frame_id": self.frame_id, + "signal_type": self.signal_type.value, + "safety_score": self.safety_score, + "violations": self.violations, + "scene_context": self.scene_context, + "ai_decision": self.ai_decision, + "correction_target": self.correction_target, + "metadata": self.metadata, + } + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "TrainingSignal": + """从字典创建实例。""" + return cls( + timestamp=data["timestamp"], + frame_id=data["frame_id"], + signal_type=SignalType(data["signal_type"]), + safety_score=data["safety_score"], + violations=data.get("violations", []), + scene_context=data.get("scene_context", {}), + ai_decision=data.get("ai_decision", {}), + correction_target=data.get("correction_target"), + metadata=data.get("metadata", {}), + ) + + +class TrainingSignalGenerator(ABC): + """训练信号生成器基类。 + + 用户可以继承此类实现自定义的训练信号生成逻辑。 + 不同的 AI 训练框架可能需要不同格式的训练数据。 + + Example: + >>> class MySignalGenerator(TrainingSignalGenerator): + ... def generate(self, safety_result, scene_state, ai_decision): + ... if not safety_result.is_safe: + ... return TrainingSignal( + ... timestamp=scene_state.timestamp, + ... frame_id=scene_state.frame_id, + ... signal_type=SignalType.NEGATIVE, + ... safety_score=safety_result.overall, + ... violations=safety_result.violations, + ... ) + ... return None + """ + + @abstractmethod + def generate( + self, + safety_result: "SafetyScore", + scene_state: "SceneState", + ai_decision: "AIDecision", + ) -> TrainingSignal | None: + """生成训练信号。 + + Args: + safety_result: 安全评分结果 + scene_state: 当前场景状态 + ai_decision: AI 的决策 + + Returns: + TrainingSignal: 训练信号,如果不需要生成则返回 None + """ + pass + + @abstractmethod + def export( + self, + signals: list[TrainingSignal], + output_path: str, + format: str = "json", + ) -> None: + """导出训练信号。 + + Args: + signals: 训练信号列表 + output_path: 输出路径 + format: 导出格式(json, tfrecord, parquet 等) + """ + pass + + def should_generate_positive(self) -> bool: + """是否生成正样本(可覆盖)。 + + 默认不生成正样本,因为正样本数量通常远大于负样本。 + 用户可以根据需要覆盖此方法。 + """ + return False + + def filter_signals( + self, + signals: list[TrainingSignal], + min_score: float = 0.0, + max_score: float = 1.0, + signal_types: list[SignalType] | None = None, + ) -> list[TrainingSignal]: + """过滤训练信号。 + + Args: + signals: 原始信号列表 + min_score: 最小安全分数 + max_score: 最大安全分数 + signal_types: 允许的信号类型 + + Returns: + 过滤后的信号列表 + """ + filtered = [] + for signal in signals: + if not (min_score <= signal.safety_score <= max_score): + continue + if signal_types and signal.signal_type not in signal_types: + continue + filtered.append(signal) + return filtered diff --git a/src/aylm/constitution/types.py b/src/aylm/constitution/types.py new file mode 100644 index 0000000..0b2b1bc --- /dev/null +++ b/src/aylm/constitution/types.py @@ -0,0 +1,136 @@ +"""宪法式 AI 类型定义。 + +定义场景状态、AI 决策等核心数据结构。 +""" + +from dataclasses import dataclass, field +from typing import Any + +import numpy as np +from numpy.typing import NDArray + + +@dataclass +class EgoState: + """自车/机器人状态。 + + Attributes: + position: 位置 [x, y, z](机器人坐标系) + velocity: 速度 [vx, vy, vz](m/s) + heading: 航向角(弧度) + speed: 标量速度(m/s) + acceleration: 加速度 [ax, ay, az](m/s²) + dimensions: 尺寸 [length, width, height](米) + """ + + position: NDArray[np.float32] + velocity: NDArray[np.float32] + heading: float + speed: float + acceleration: NDArray[np.float32] | None = None + dimensions: NDArray[np.float32] | None = None + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "position": self.position.tolist(), + "velocity": self.velocity.tolist(), + "heading": self.heading, + "speed": self.speed, + "acceleration": ( + self.acceleration.tolist() if self.acceleration is not None else None + ), + "dimensions": ( + self.dimensions.tolist() if self.dimensions is not None else None + ), + } + + +@dataclass +class TrajectoryPoint: + """轨迹点。""" + + position: NDArray[np.float32] # [x, y, z] + velocity: NDArray[np.float32] | None = None # [vx, vy, vz] + timestamp: float = 0.0 # 相对时间(秒) + + +@dataclass +class AIDecision: + """AI 决策。 + + 表示端到端 AI 系统输出的决策,可以是轨迹规划或控制指令。 + + Attributes: + decision_type: 决策类型(trajectory, control, waypoint) + trajectory: 规划轨迹(轨迹点列表) + control: 控制指令(steering, throttle, brake) + target_speed: 目标速度(m/s) + confidence: 决策置信度 + metadata: 额外元数据 + """ + + decision_type: str # "trajectory", "control", "waypoint" + trajectory: list[TrajectoryPoint] = field(default_factory=list) + control: dict[str, float] = field(default_factory=dict) # steering, throttle, brake + target_speed: float | None = None + confidence: float = 1.0 + metadata: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "decision_type": self.decision_type, + "trajectory": [ + { + "position": p.position.tolist(), + "velocity": p.velocity.tolist() if p.velocity is not None else None, + "timestamp": p.timestamp, + } + for p in self.trajectory + ], + "control": self.control, + "target_speed": self.target_speed, + "confidence": self.confidence, + "metadata": self.metadata, + } + + +@dataclass +class SceneState: + """场景状态。 + + 包含当前帧的所有感知信息,供宪法原则评估使用。 + + Attributes: + frame_id: 帧 ID + timestamp: 时间戳(秒) + ego_state: 自车状态 + obstacles: 障碍物列表(来自 A-YLM 感知模块) + lane_boundaries: 车道边界(可选) + traffic_signs: 交通标志(可选) + metadata: 额外元数据 + """ + + frame_id: int + timestamp: float + ego_state: EgoState + obstacles: list[Any] = field(default_factory=list) # ObstacleBox3D 列表 + lane_boundaries: list[Any] | None = None + traffic_signs: list[Any] | None = None + metadata: dict[str, Any] = field(default_factory=dict) + + def to_dict(self) -> dict[str, Any]: + """转换为字典格式。""" + return { + "frame_id": self.frame_id, + "timestamp": self.timestamp, + "ego_state": self.ego_state.to_dict(), + "obstacles": [ + obs.to_dict() if hasattr(obs, "to_dict") else obs + for obs in self.obstacles + ], + "lane_boundaries": self.lane_boundaries, + "traffic_signs": self.traffic_signs, + "metadata": self.metadata, + } diff --git a/src/aylm/tools/__init__.py b/src/aylm/tools/__init__.py index b73e5de..f46f532 100644 --- a/src/aylm/tools/__init__.py +++ b/src/aylm/tools/__init__.py @@ -6,7 +6,22 @@ transform_for_navigation, transform_obstacle_center, ) +from .motion_estimator import ( + KalmanConfig, + MotionEstimator, + MotionVector, + TrackedObject3D, + create_tracked_object, +) +from .multiframe_fusion import ( + FramePose, + FusionResult, + MultiframeFusion, + RegistrationConfig, + RegistrationResult, +) from .object_detector import DetectorConfig, ObjectDetector +from .object_tracker import MultiObjectTracker, TrackedObject, TrackerConfig from .obstacle_marker import ( ObstacleBox3D, ObstacleMarker, @@ -79,4 +94,20 @@ "opencv_to_robot", "robot_to_opencv", "transform_obstacle_center", + # 多帧融合 + "MultiframeFusion", + "RegistrationConfig", + "RegistrationResult", + "FramePose", + "FusionResult", + # 运动估计 + "MotionEstimator", + "MotionVector", + "TrackedObject3D", + "KalmanConfig", + "create_tracked_object", + # 目标跟踪 + "MultiObjectTracker", + "TrackedObject", + "TrackerConfig", ] diff --git a/src/aylm/tools/coordinate_utils.py b/src/aylm/tools/coordinate_utils.py index 7c69813..295edb8 100644 --- a/src/aylm/tools/coordinate_utils.py +++ b/src/aylm/tools/coordinate_utils.py @@ -223,7 +223,7 @@ def _read_ply( raise ValueError("PLY文件数据不完整") values = line.split() points.append([float(values[0]), float(values[1]), float(values[2])]) - if has_colors and len(values) >= 6: + if has_colors and colors is not None and len(values) >= 6: colors.append([int(values[3]), int(values[4]), int(values[5])]) return header_lines, np.array(points), has_colors, colors diff --git a/src/aylm/tools/motion_estimator.py b/src/aylm/tools/motion_estimator.py new file mode 100644 index 0000000..e67046d --- /dev/null +++ b/src/aylm/tools/motion_estimator.py @@ -0,0 +1,408 @@ +"""运动矢量估计器模块。 + +基于连续帧的 3D 位置变化计算速度,使用 Kalman 滤波器平滑轨迹。 +""" + +from dataclasses import dataclass +from typing import Optional + +import numpy as np +from numpy.typing import NDArray + +from .coordinate_utils import opencv_to_robot +from .semantic_types import SemanticLabel + + +@dataclass +class MotionVector: + """运动矢量。""" + + velocity_cv: NDArray[np.float32] # OpenCV 坐标系速度 [vx, vy, vz] m/s + velocity_robot: NDArray[np.float32] # 机器人坐标系速度 [vx, vy, vz] m/s + speed: float # 标量速度 m/s + heading: float # 航向角(弧度,相对于机器人前进方向) + is_stationary: bool # 是否静止(速度 < 阈值) + + +@dataclass +class TrackedObject3D: + """3D 跟踪对象。""" + + track_id: int + center_cv: NDArray[np.float32] # OpenCV 坐标系中心 + center_robot: NDArray[np.float32] # 机器人坐标系中心 + dimensions: NDArray[np.float32] # [width, height, depth] + motion: Optional[MotionVector] # 运动信息 + semantic_label: SemanticLabel + confidence: float + frame_id: int + timestamp: float # 秒 + + +@dataclass +class KalmanConfig: + """Kalman 滤波器配置。""" + + process_noise: float = 0.1 # 过程噪声 + measurement_noise: float = 0.5 # 观测噪声 + initial_covariance: float = 1.0 # 初始协方差 + + +@dataclass +class _TrackState: + """单个轨迹的 Kalman 滤波器状态。""" + + # 状态向量: [x, y, z, vx, vy, vz] + state: NDArray[np.float64] + # 协方差矩阵 6x6 + covariance: NDArray[np.float64] + # 上一帧 ID + last_frame_id: int + # 上一帧时间戳 + last_timestamp: float + # 初始化标志 + initialized: bool = False + + +class MotionEstimator: + """运动矢量估计器。 + + 使用 Kalman 滤波器平滑轨迹并估计速度。 + + 状态向量: [x, y, z, vx, vy, vz] + 观测向量: [x, y, z] + """ + + def __init__( + self, + fps: float = 30.0, + stationary_threshold: float = 0.1, + kalman_config: Optional[KalmanConfig] = None, + ): + """初始化运动估计器。 + + Args: + fps: 帧率,用于计算默认时间间隔 + stationary_threshold: 静止判定阈值(m/s) + kalman_config: Kalman 滤波器配置 + """ + self.fps = fps + self.dt_default = 1.0 / fps + self.stationary_threshold = stationary_threshold + self.config = kalman_config or KalmanConfig() + + # 轨迹状态字典 {track_id: _TrackState} + self._tracks: dict[int, _TrackState] = {} + + # 预计算 Kalman 矩阵 + self._init_kalman_matrices() + + def _init_kalman_matrices(self) -> None: + """初始化 Kalman 滤波器矩阵。""" + # 观测矩阵 H: 3x6,只观测位置 + self._H = np.array( + [ + [1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0], + ], + dtype=np.float64, + ) + + # 观测噪声协方差 R: 3x3 + self._R = np.eye(3, dtype=np.float64) * self.config.measurement_noise + + def _get_transition_matrix(self, dt: float) -> NDArray[np.float64]: + """获取状态转移矩阵 F。 + + Args: + dt: 时间间隔(秒) + + Returns: + 6x6 状态转移矩阵 + """ + return np.array( + [ + [1, 0, 0, dt, 0, 0], + [0, 1, 0, 0, dt, 0], + [0, 0, 1, 0, 0, dt], + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1], + ], + dtype=np.float64, + ) + + def _get_process_noise(self, dt: float) -> NDArray[np.float64]: + """获取过程噪声协方差矩阵 Q。 + + 使用离散白噪声加速度模型。 + + Args: + dt: 时间间隔(秒) + + Returns: + 6x6 过程噪声协方差矩阵 + """ + q = self.config.process_noise + dt2 = dt * dt + dt3 = dt2 * dt + dt4 = dt3 * dt + + # 离散白噪声加速度模型 + return ( + np.array( + [ + [dt4 / 4, 0, 0, dt3 / 2, 0, 0], + [0, dt4 / 4, 0, 0, dt3 / 2, 0], + [0, 0, dt4 / 4, 0, 0, dt3 / 2], + [dt3 / 2, 0, 0, dt2, 0, 0], + [0, dt3 / 2, 0, 0, dt2, 0], + [0, 0, dt3 / 2, 0, 0, dt2], + ], + dtype=np.float64, + ) + * q + ) + + def _init_track( + self, position: NDArray, frame_id: int, timestamp: float + ) -> _TrackState: + """初始化新轨迹。 + + Args: + position: 初始位置 [x, y, z] + frame_id: 帧 ID + timestamp: 时间戳 + + Returns: + 新的轨迹状态 + """ + state = np.zeros(6, dtype=np.float64) + state[:3] = position + + covariance = np.eye(6, dtype=np.float64) * self.config.initial_covariance + # 速度初始不确定性更大 + covariance[3:, 3:] *= 10.0 + + return _TrackState( + state=state, + covariance=covariance, + last_frame_id=frame_id, + last_timestamp=timestamp, + initialized=True, + ) + + def _predict(self, track: _TrackState, dt: float) -> None: + """Kalman 预测步骤。 + + Args: + track: 轨迹状态 + dt: 时间间隔 + """ + F = self._get_transition_matrix(dt) + Q = self._get_process_noise(dt) + + # 状态预测: x' = F * x + track.state = F @ track.state + + # 协方差预测: P' = F * P * F^T + Q + track.covariance = F @ track.covariance @ F.T + Q + + def _update_kalman( + self, track: _TrackState, measurement: NDArray[np.float64] + ) -> None: + """Kalman 更新步骤。 + + Args: + track: 轨迹状态 + measurement: 观测值 [x, y, z] + """ + H = self._H + R = self._R + + # 创新(残差): y = z - H * x + y = measurement - H @ track.state + + # 创新协方差: S = H * P * H^T + R + S = H @ track.covariance @ H.T + R + + # Kalman 增益: K = P * H^T * S^(-1) + K = track.covariance @ H.T @ np.linalg.inv(S) + + # 状态更新: x = x + K * y + track.state = track.state + K @ y + + # 协方差更新: P = (I - K * H) * P + identity = np.eye(6, dtype=np.float64) + track.covariance = (identity - K @ H) @ track.covariance + + def update( + self, + track_id: int, + position_cv: NDArray, + frame_id: int, + timestamp: Optional[float] = None, + ) -> Optional[MotionVector]: + """更新轨迹,返回运动矢量。 + + Args: + track_id: 轨迹 ID + position_cv: OpenCV 坐标系位置 [x, y, z] + frame_id: 帧 ID + timestamp: 时间戳(秒),如果为 None 则根据帧率计算 + + Returns: + 运动矢量,如果是第一帧则返回 None + """ + position = np.asarray(position_cv, dtype=np.float64) + + # 计算时间戳 + if timestamp is None: + timestamp = frame_id / self.fps + + # 检查是否是新轨迹 + if track_id not in self._tracks: + self._tracks[track_id] = self._init_track(position, frame_id, timestamp) + return None + + track = self._tracks[track_id] + + # 计算时间间隔 + dt = timestamp - track.last_timestamp + if dt <= 0: + dt = self.dt_default + + # Kalman 预测 + self._predict(track, dt) + + # Kalman 更新 + self._update_kalman(track, position) + + # 更新时间信息 + track.last_frame_id = frame_id + track.last_timestamp = timestamp + + # 提取速度 + velocity_cv = track.state[3:6].astype(np.float32) + + # 转换到机器人坐标系 + velocity_robot = opencv_to_robot(velocity_cv).astype(np.float32) + + # 计算标量速度 + speed = float(np.linalg.norm(velocity_cv)) + + # 计算航向角(在机器人坐标系中,相对于 X 轴/前进方向) + # heading = atan2(vy, vx),范围 [-pi, pi] + heading = float(np.arctan2(velocity_robot[1], velocity_robot[0])) + + # 判断是否静止 + is_stationary = speed < self.stationary_threshold + + return MotionVector( + velocity_cv=velocity_cv, + velocity_robot=velocity_robot, + speed=speed, + heading=heading, + is_stationary=is_stationary, + ) + + def predict(self, track_id: int, dt: float) -> Optional[NDArray[np.float64]]: + """预测未来位置。 + + Args: + track_id: 轨迹 ID + dt: 预测时间间隔(秒) + + Returns: + 预测的 OpenCV 坐标系位置 [x, y, z],如果轨迹不存在则返回 None + """ + if track_id not in self._tracks: + return None + + track = self._tracks[track_id] + F = self._get_transition_matrix(dt) + + # 预测状态 + predicted_state = F @ track.state + + return predicted_state[:3] + + def get_velocity(self, track_id: int) -> Optional[NDArray[np.float64]]: + """获取当前速度估计。 + + Args: + track_id: 轨迹 ID + + Returns: + OpenCV 坐标系速度 [vx, vy, vz],如果轨迹不存在则返回 None + """ + if track_id not in self._tracks: + return None + + return self._tracks[track_id].state[3:6].copy() + + def remove_track(self, track_id: int) -> bool: + """移除轨迹。 + + Args: + track_id: 轨迹 ID + + Returns: + 是否成功移除 + """ + if track_id in self._tracks: + del self._tracks[track_id] + return True + return False + + def clear(self) -> None: + """清除所有轨迹。""" + self._tracks.clear() + + @property + def active_tracks(self) -> list[int]: + """获取活跃轨迹 ID 列表。""" + return list(self._tracks.keys()) + + +def create_tracked_object( + track_id: int, + center_cv: NDArray, + dimensions: NDArray, + semantic_label: SemanticLabel, + confidence: float, + frame_id: int, + timestamp: float, + motion: Optional[MotionVector] = None, +) -> TrackedObject3D: + """创建 3D 跟踪对象。 + + Args: + track_id: 轨迹 ID + center_cv: OpenCV 坐标系中心 + dimensions: 尺寸 [width, height, depth] + semantic_label: 语义标签 + confidence: 置信度 + frame_id: 帧 ID + timestamp: 时间戳 + motion: 运动信息 + + Returns: + TrackedObject3D 实例 + """ + center_cv = np.asarray(center_cv, dtype=np.float32) + center_robot = opencv_to_robot(center_cv).astype(np.float32) + dimensions = np.asarray(dimensions, dtype=np.float32) + + return TrackedObject3D( + track_id=track_id, + center_cv=center_cv, + center_robot=center_robot, + dimensions=dimensions, + motion=motion, + semantic_label=semantic_label, + confidence=confidence, + frame_id=frame_id, + timestamp=timestamp, + ) diff --git a/src/aylm/tools/multiframe_fusion.py b/src/aylm/tools/multiframe_fusion.py new file mode 100644 index 0000000..002a741 --- /dev/null +++ b/src/aylm/tools/multiframe_fusion.py @@ -0,0 +1,597 @@ +"""多帧点云配准融合模块。 + +使用 Open3D 实现 ICP 配准和位姿图优化,将多帧点云融合成全局地图。 +""" + +from __future__ import annotations + +import json +import logging +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +import numpy as np +from numpy.typing import NDArray + +from .pointcloud_voxelizer import PointCloud + +logger = logging.getLogger(__name__) + +# 检查 Open3D 是否可用 +try: + import open3d as o3d + + HAS_OPEN3D = True +except ImportError: + HAS_OPEN3D = False + o3d = None # type: ignore[assignment] + + +@dataclass +class RegistrationConfig: + """配准配置参数。""" + + # ICP 配准参数 + icp_max_correspondence_distance: float = 0.05 # ICP 最大对应点距离(米) + icp_max_iteration: int = 50 # ICP 最大迭代次数 + icp_relative_fitness: float = 1e-6 # 相对适应度收敛阈值 + icp_relative_rmse: float = 1e-6 # 相对 RMSE 收敛阈值 + + # 特征提取参数 + voxel_size_for_features: float = 0.05 # 特征提取用的体素大小 + fpfh_radius: float = 0.25 # FPFH 特征半径 + fpfh_max_nn: int = 100 # FPFH 最大近邻数 + normal_radius: float = 0.1 # 法向量估计半径 + + # 位姿图优化参数 + pose_graph_edge_prune_threshold: float = 0.25 # 边修剪阈值 + pose_graph_preference_loop_closure: float = 0.1 # 闭环偏好 + + # 全局融合参数 + fusion_voxel_size: float = 0.02 # 融合后体素下采样大小 + + # 配准质量阈值 + min_fitness: float = 0.3 # 最小配准适应度 + min_points: int = 1000 # 最小点数要求 + + +@dataclass +class FramePose: + """单帧位姿信息。""" + + frame_index: int # 帧索引 + transformation: NDArray[np.float64] # 4x4 变换矩阵 + fitness: float = 0.0 # 配准适应度 + rmse: float = 0.0 # 配准 RMSE + is_keyframe: bool = False # 是否为关键帧 + + def to_dict(self) -> dict[str, Any]: + """转换为字典。""" + return { + "index": self.frame_index, + "transformation": self.transformation.tolist(), + "fitness": self.fitness, + "rmse": self.rmse, + "is_keyframe": self.is_keyframe, + } + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> FramePose: + """从字典创建。""" + return cls( + frame_index=data["index"], + transformation=np.array(data["transformation"]), + fitness=data.get("fitness", 0.0), + rmse=data.get("rmse", 0.0), + is_keyframe=data.get("is_keyframe", False), + ) + + +@dataclass +class RegistrationResult: + """配准结果。""" + + transformation: NDArray[np.float64] # 4x4 变换矩阵 + fitness: float # 适应度 + inlier_rmse: float # 内点 RMSE + correspondence_count: int # 对应点数量 + + +@dataclass +class FusionResult: + """融合结果。""" + + fused_pointcloud: PointCloud # 融合后的点云 + frame_poses: list[FramePose] = field(default_factory=list) # 所有帧的位姿 + total_frames: int = 0 # 总帧数 + successful_registrations: int = 0 # 成功配准数 + fusion_time: float = 0.0 # 融合耗时 + + +class MultiframeFusion: + """多帧点云配准融合器。 + + 使用 ICP 算法进行相邻帧配准,位姿图优化进行全局一致性调整, + 最终将所有帧变换到世界坐标系并合并。 + + Example: + >>> config = RegistrationConfig(icp_max_correspondence_distance=0.05) + >>> fusion = MultiframeFusion(config) + >>> result = fusion.fuse_sequence(pointcloud_list) + >>> fusion.save_fused_map(result, "fused_map.ply") + """ + + def __init__(self, config: RegistrationConfig | None = None): + """初始化融合器。 + + Args: + config: 配准配置,为 None 时使用默认配置 + """ + if not HAS_OPEN3D: + raise ImportError("Open3D 未安装,请运行: pip install open3d") + + self.config = config or RegistrationConfig() + logger.info("MultiframeFusion 初始化完成") + + def fuse_sequence( + self, + pointclouds: list[PointCloud], + initial_poses: list[NDArray[np.float64]] | None = None, + ) -> FusionResult: + """融合点云序列。 + + Args: + pointclouds: 点云列表(按时间顺序) + initial_poses: 可选的初始位姿估计 + + Returns: + FusionResult: 融合结果 + """ + start_time = time.time() + n_frames = len(pointclouds) + + if n_frames == 0: + logger.warning("输入点云列表为空") + return FusionResult(fused_pointcloud=PointCloud(np.zeros((0, 3)))) + + if n_frames == 1: + logger.info("只有一帧,直接返回") + pose = FramePose(0, np.eye(4), 1.0, 0.0, True) + return FusionResult( + fused_pointcloud=pointclouds[0], + frame_poses=[pose], + total_frames=1, + successful_registrations=1, + fusion_time=time.time() - start_time, + ) + + logger.info(f"开始融合 {n_frames} 帧点云") + + # 1. 预处理所有点云 + logger.info("预处理点云...") + processed = [] + valid_indices = [] + for i, pc in enumerate(pointclouds): + if len(pc.points) < self.config.min_points: + logger.warning(f"帧 {i} 点数不足 ({len(pc.points)}), 跳过") + continue + pcd, fpfh = self._preprocess_pointcloud(pc) + processed.append((pcd, fpfh, pc)) + valid_indices.append(i) + + if len(processed) < 2: + logger.error("有效帧数不足,无法配准") + return FusionResult(fused_pointcloud=PointCloud(np.zeros((0, 3)))) + + # 2. 相邻帧配准(前一帧 -> 后一帧) + logger.info("执行相邻帧配准...") + pairwise_results: list[RegistrationResult | None] = [] + for i in range(len(processed) - 1): + source_pcd, source_fpfh, _ = processed[i] + target_pcd, target_fpfh, _ = processed[i + 1] + + result = self._register_pair_o3d( + source_pcd, target_pcd, source_fpfh, target_fpfh + ) + + if result.fitness < self.config.min_fitness: + logger.warning( + f"帧 {valid_indices[i]} -> {valid_indices[i+1]} " + f"配准质量低 (fitness={result.fitness:.3f})" + ) + pairwise_results.append(result) + logger.debug( + f"帧 {valid_indices[i]} -> {valid_indices[i+1]}: " + f"fitness={result.fitness:.3f}, rmse={result.inlier_rmse:.4f}" + ) + + # 3. 构建位姿图 + logger.info("构建位姿图...") + pose_graph = self._build_pose_graph(processed, pairwise_results) + + # 4. 优化位姿图 + logger.info("优化位姿图...") + pose_graph = self._optimize_pose_graph(pose_graph) + + # 5. 提取优化后的位姿 + frame_poses = [] + successful = 0 + for i, node in enumerate(pose_graph.nodes): + original_idx = valid_indices[i] + prev_result = pairwise_results[i - 1] if i > 0 else None + pose = FramePose( + frame_index=original_idx, + transformation=np.asarray(node.pose), + fitness=prev_result.fitness if prev_result else 1.0, + rmse=prev_result.inlier_rmse if prev_result else 0.0, + is_keyframe=(i == 0), + ) + frame_poses.append(pose) + if i == 0 or ( + prev_result and prev_result.fitness >= self.config.min_fitness + ): + successful += 1 + + # 6. 变换并合并点云 + logger.info("合并点云...") + original_pcs = [p[2] for p in processed] + fused_pc = self._transform_and_merge(original_pcs, frame_poses) + + # 7. 体素下采样 + logger.info("体素下采样...") + fused_pc = self._voxel_downsample(fused_pc, self.config.fusion_voxel_size) + + fusion_time = time.time() - start_time + logger.info( + f"融合完成: {len(fused_pc.points)} 点, " + f"{successful}/{len(processed)} 帧成功, 耗时 {fusion_time:.2f}s" + ) + + return FusionResult( + fused_pointcloud=fused_pc, + frame_poses=frame_poses, + total_frames=n_frames, + successful_registrations=successful, + fusion_time=fusion_time, + ) + + def fuse_from_directory( + self, + input_dir: Path, + pattern: str = "vox_*.ply", + ) -> FusionResult: + """从目录加载并融合点云序列。 + + Args: + input_dir: 输入目录 + pattern: 文件匹配模式 + + Returns: + FusionResult: 融合结果 + """ + input_dir = Path(input_dir) + ply_files = sorted(input_dir.glob(pattern)) + + if not ply_files: + logger.error(f"未找到匹配 {pattern} 的文件: {input_dir}") + return FusionResult(fused_pointcloud=PointCloud(np.zeros((0, 3)))) + + logger.info(f"加载 {len(ply_files)} 个点云文件") + pointclouds = [] + for ply_path in ply_files: + pc = PointCloud.from_ply(ply_path) + pointclouds.append(pc) + logger.debug(f"加载 {ply_path.name}: {len(pc.points)} 点") + + return self.fuse_sequence(pointclouds) + + def register_pair( + self, + source: PointCloud, + target: PointCloud, + initial_transform: NDArray[np.float64] | None = None, + ) -> RegistrationResult: + """配准两帧点云。 + + Args: + source: 源点云 + target: 目标点云 + initial_transform: 初始变换估计 + + Returns: + RegistrationResult: 配准结果 + """ + source_pcd, source_fpfh = self._preprocess_pointcloud(source) + target_pcd, target_fpfh = self._preprocess_pointcloud(target) + + if initial_transform is not None: + # 直接使用 ICP 精配准 + return self._fine_registration(source_pcd, target_pcd, initial_transform) + else: + # 粗配准 + 精配准 + return self._register_pair_o3d( + source_pcd, target_pcd, source_fpfh, target_fpfh + ) + + def save_fused_map( + self, + result: FusionResult, + output_path: Path, + include_poses: bool = True, + ) -> None: + """保存融合后的地图。 + + Args: + result: 融合结果 + output_path: 输出 PLY 文件路径 + include_poses: 是否同时保存位姿文件 + """ + output_path = Path(output_path) + output_path.parent.mkdir(parents=True, exist_ok=True) + + # 保存点云 + result.fused_pointcloud.to_ply(output_path) + logger.info(f"融合地图已保存: {output_path}") + + # 保存位姿 + if include_poses and result.frame_poses: + poses_path = output_path.with_suffix(".poses.json") + self.save_poses(result.frame_poses, poses_path) + + def save_poses( + self, + poses: list[FramePose], + output_path: Path, + ) -> None: + """保存位姿轨迹到 JSON 文件。 + + Args: + poses: 位姿列表 + output_path: 输出路径 + """ + output_path = Path(output_path) + + data = { + "version": "1.0", + "coordinate_system": "opencv", + "frames": [p.to_dict() for p in poses], + "statistics": { + "total_frames": len(poses), + "average_fitness": np.mean([p.fitness for p in poses]), + "average_rmse": np.mean([p.rmse for p in poses]), + }, + } + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + logger.info(f"位姿轨迹已保存: {output_path}") + + # ========== 内部方法 ========== + + def _to_o3d(self, pc: PointCloud) -> o3d.geometry.PointCloud: + """转换为 Open3D 点云格式。""" + pcd = o3d.geometry.PointCloud() + pcd.points = o3d.utility.Vector3dVector(pc.points.astype(np.float64)) + if pc.colors is not None: + colors = pc.colors.astype(np.float64) + if colors.max() > 1.0: + colors = colors / 255.0 + pcd.colors = o3d.utility.Vector3dVector(colors) + return pcd + + def _from_o3d(self, pcd: o3d.geometry.PointCloud) -> PointCloud: + """从 Open3D 点云格式转换。""" + points = np.asarray(pcd.points) + colors = None + if pcd.has_colors(): + colors = (np.asarray(pcd.colors) * 255).astype(np.uint8) + return PointCloud(points=points, colors=colors) + + def _preprocess_pointcloud( + self, + pc: PointCloud, + ) -> tuple[o3d.geometry.PointCloud, o3d.pipelines.registration.Feature]: + """预处理点云:下采样 + 计算法向量 + 提取 FPFH 特征。""" + pcd = self._to_o3d(pc) + + # 体素下采样 + pcd_down = pcd.voxel_down_sample(self.config.voxel_size_for_features) + + # 估计法向量 + pcd_down.estimate_normals( + o3d.geometry.KDTreeSearchParamHybrid( + radius=self.config.normal_radius, max_nn=30 + ) + ) + + # 计算 FPFH 特征 + fpfh = o3d.pipelines.registration.compute_fpfh_feature( + pcd_down, + o3d.geometry.KDTreeSearchParamHybrid( + radius=self.config.fpfh_radius, max_nn=self.config.fpfh_max_nn + ), + ) + + return pcd_down, fpfh + + def _register_pair_o3d( + self, + source_pcd: o3d.geometry.PointCloud, + target_pcd: o3d.geometry.PointCloud, + source_fpfh: o3d.pipelines.registration.Feature, + target_fpfh: o3d.pipelines.registration.Feature, + ) -> RegistrationResult: + """配准两帧点云(粗配准 + 精配准)。""" + # 粗配准: RANSAC + coarse_result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching( + source_pcd, + target_pcd, + source_fpfh, + target_fpfh, + mutual_filter=True, + max_correspondence_distance=self.config.icp_max_correspondence_distance * 2, + estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint( + False + ), + ransac_n=4, + checkers=[ + o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9), + o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance( + self.config.icp_max_correspondence_distance * 2 + ), + ], + criteria=o3d.pipelines.registration.RANSACConvergenceCriteria( + max_iteration=4000000, confidence=0.999 + ), + ) + + # 精配准: Point-to-Plane ICP + return self._fine_registration( + source_pcd, target_pcd, coarse_result.transformation + ) + + def _fine_registration( + self, + source_pcd: o3d.geometry.PointCloud, + target_pcd: o3d.geometry.PointCloud, + initial_transform: NDArray[np.float64], + ) -> RegistrationResult: + """精配准:Point-to-Plane ICP。""" + # 确保有法向量 + if not target_pcd.has_normals(): + target_pcd.estimate_normals( + o3d.geometry.KDTreeSearchParamHybrid( + radius=self.config.normal_radius, max_nn=30 + ) + ) + + result = o3d.pipelines.registration.registration_icp( + source_pcd, + target_pcd, + self.config.icp_max_correspondence_distance, + initial_transform, + o3d.pipelines.registration.TransformationEstimationPointToPlane(), + o3d.pipelines.registration.ICPConvergenceCriteria( + relative_fitness=self.config.icp_relative_fitness, + relative_rmse=self.config.icp_relative_rmse, + max_iteration=self.config.icp_max_iteration, + ), + ) + + return RegistrationResult( + transformation=np.asarray(result.transformation), + fitness=result.fitness, + inlier_rmse=result.inlier_rmse, + correspondence_count=len(result.correspondence_set), + ) + + def _build_pose_graph( + self, + processed: list[tuple], + pairwise_results: list[RegistrationResult | None], + ) -> o3d.pipelines.registration.PoseGraph: + """构建位姿图。 + + 以第一帧为世界坐标系原点,累积后续帧的位姿。 + 配准结果 T_{i->i+1} 表示把帧i变换到帧i+1。 + 我们需要的是把每帧变换到帧0(世界坐标系)。 + """ + pose_graph = o3d.pipelines.registration.PoseGraph() + n_frames = len(processed) + + # 第一帧位姿为单位矩阵(世界坐标系原点) + pose_graph.nodes.append(o3d.pipelines.registration.PoseGraphNode(np.eye(4))) + + # 累积位姿:T_0^i = T_0^{i-1} @ inv(T_{i-1->i}) + # 因为 T_{i-1->i} 把帧i-1变换到帧i,所以逆变换把帧i变换到帧i-1 + cumulative_pose = np.eye(4) + for i in range(1, n_frames): + result = pairwise_results[i - 1] + if result is not None: + # T_{i-1->i} 的逆,把帧i变换到帧i-1的坐标系 + inv_transform = np.linalg.inv(result.transformation) + cumulative_pose = cumulative_pose @ inv_transform + pose_graph.nodes.append( + o3d.pipelines.registration.PoseGraphNode(cumulative_pose.copy()) + ) + + # 添加边(相邻帧) + for i in range(n_frames - 1): + result = pairwise_results[i] + if result is None: + continue + + # 信息矩阵(基于配准质量) + information = ( + np.eye(6) * result.fitness if result.fitness > 0 else np.eye(6) * 0.1 + ) + + pose_graph.edges.append( + o3d.pipelines.registration.PoseGraphEdge( + i, + i + 1, + result.transformation, + information, + uncertain=False, + ) + ) + + return pose_graph + + def _optimize_pose_graph( + self, + pose_graph: o3d.pipelines.registration.PoseGraph, + ) -> o3d.pipelines.registration.PoseGraph: + """优化位姿图。""" + option = o3d.pipelines.registration.GlobalOptimizationOption( + max_correspondence_distance=self.config.icp_max_correspondence_distance, + edge_prune_threshold=self.config.pose_graph_edge_prune_threshold, + preference_loop_closure=self.config.pose_graph_preference_loop_closure, + reference_node=0, + ) + + o3d.pipelines.registration.global_optimization( + pose_graph, + o3d.pipelines.registration.GlobalOptimizationLevenbergMarquardt(), + o3d.pipelines.registration.GlobalOptimizationConvergenceCriteria(), + option, + ) + + return pose_graph + + def _transform_and_merge( + self, + pointclouds: list[PointCloud], + poses: list[FramePose], + ) -> PointCloud: + """将所有点云变换到世界坐标系并合并。""" + all_points = [] + all_colors = [] + has_colors = pointclouds[0].colors is not None + + for pc, pose in zip(pointclouds, poses): + # 应用变换 + points_h = np.hstack([pc.points, np.ones((len(pc.points), 1))]) + transformed = (pose.transformation @ points_h.T).T[:, :3] + all_points.append(transformed) + + if has_colors and pc.colors is not None: + all_colors.append(pc.colors) + + merged_points = np.vstack(all_points) + merged_colors = np.vstack(all_colors) if has_colors else None + + return PointCloud(points=merged_points, colors=merged_colors) + + def _voxel_downsample( + self, + pc: PointCloud, + voxel_size: float, + ) -> PointCloud: + """体素下采样去重。""" + pcd = self._to_o3d(pc) + pcd_down = pcd.voxel_down_sample(voxel_size) + return self._from_o3d(pcd_down) diff --git a/src/aylm/tools/object_detector.py b/src/aylm/tools/object_detector.py index 086be78..72a94fa 100644 --- a/src/aylm/tools/object_detector.py +++ b/src/aylm/tools/object_detector.py @@ -7,11 +7,14 @@ import logging from dataclasses import dataclass, field from pathlib import Path -from typing import Optional, Union +from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union import numpy as np from numpy.typing import NDArray +if TYPE_CHECKING: + from ultralytics import YOLO + from .semantic_types import COCO_TO_SEMANTIC, Detection2D, SemanticLabel logger = logging.getLogger(__name__) @@ -22,7 +25,7 @@ class DetectorConfig: """检测器配置参数。""" model_name: str = "yolo11n-seg.pt" # YOLO11 模型名称 - confidence_threshold: float = 0.5 # 置信度阈值 + confidence_threshold: float = 0.25 # 置信度阈值(降低以检测更多目标) iou_threshold: float = 0.45 # NMS IoU 阈值 device: str = "auto" # 设备:auto/cuda/mps/cpu half_precision: bool = True # 是否使用半精度(FP16) @@ -41,7 +44,7 @@ class ObjectDetector: """ # 默认检测类别:人、自行车、汽车、摩托车、公交车、卡车 - DEFAULT_CLASSES: list[int] = [0, 1, 2, 3, 5, 7] + DEFAULT_CLASSES: ClassVar[list[int]] = [0, 1, 2, 3, 5, 7] def __init__(self, config: Optional[DetectorConfig] = None): """初始化检测器。 @@ -50,7 +53,7 @@ def __init__(self, config: Optional[DetectorConfig] = None): config: 检测器配置,为 None 时使用默认配置 """ self.config = config or DetectorConfig() - self._model = None + self._model: Optional[YOLO] = None self._device = self._detect_device() # 如果未指定类别,使用默认类别 @@ -142,7 +145,7 @@ def unload(self) -> None: def detect( self, - image: NDArray[np.uint8], + image: NDArray[Any], return_masks: bool = True, ) -> list[Detection2D]: """执行目标检测。 @@ -263,13 +266,13 @@ def __enter__(self) -> "ObjectDetector": self.load() return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__(self, _exc_type, _exc_val, _exc_tb) -> None: """上下文管理器出口,自动卸载模型。""" self.unload() def save_detection_image( self, - image: NDArray[np.uint8], + image: NDArray[Any], detections: list[Detection2D], output_path: Union[str, Path], draw_masks: bool = True, @@ -318,7 +321,7 @@ def save_detection_image( # 绘制标签背景 label_text = f"{label_name} {det.confidence:.2f}" - (text_w, text_h), baseline = cv2.getTextSize( + (text_w, text_h), _baseline = cv2.getTextSize( label_text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2 ) cv2.rectangle( @@ -347,7 +350,9 @@ def save_detection_image( ) mask_colored = np.zeros_like(result_image) mask_colored[mask_resized > 0] = color - result_image = cv2.addWeighted(result_image, 1.0, mask_colored, 0.3, 0) + result_image = cv2.addWeighted( + result_image, 1.0, mask_colored, 0.3, 0 + ) # type: ignore[assignment] # 保存图片 output_path.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/aylm/tools/object_tracker.py b/src/aylm/tools/object_tracker.py new file mode 100644 index 0000000..63b111c --- /dev/null +++ b/src/aylm/tools/object_tracker.py @@ -0,0 +1,357 @@ +"""多目标跟踪模块。 + +基于 IoU 匹配的多目标跟踪器,使用匈牙利算法进行检测-轨迹匹配。 +支持轨迹的创建、更新和删除。 +""" + +import logging +from dataclasses import dataclass, field +from typing import Optional + +import numpy as np +from numpy.typing import NDArray +from scipy.optimize import linear_sum_assignment + +from .semantic_types import Detection2D + +logger = logging.getLogger(__name__) + + +@dataclass +class TrackedObject: + """跟踪目标。""" + + track_id: int # 唯一跟踪 ID + bbox: NDArray[np.float32] # [x1, y1, x2, y2] + class_id: int # COCO 类别 ID + confidence: float # 置信度 + age: int = 1 # 跟踪帧数 + hits: int = 1 # 成功匹配次数 + time_since_update: int = 0 # 距上次更新的帧数 + + def __post_init__(self) -> None: + """确保 bbox 是正确的类型。""" + if not isinstance(self.bbox, np.ndarray): + self.bbox = np.array(self.bbox, dtype=np.float32) + elif self.bbox.dtype != np.float32: + self.bbox = self.bbox.astype(np.float32) + + +@dataclass +class TrackerConfig: + """跟踪器配置参数。""" + + max_age: int = 30 # 轨迹最大存活帧数(无匹配时) + min_hits: int = 3 # 轨迹确认所需最小匹配次数 + iou_threshold: float = 0.3 # IoU 匹配阈值 + + +@dataclass +class _Track: + """内部轨迹状态。""" + + track_id: int + bbox: NDArray[np.float32] + class_id: int + confidence: float + age: int = 1 + hits: int = 1 + time_since_update: int = 0 + velocity: NDArray[np.float32] = field( + default_factory=lambda: np.zeros(4, dtype=np.float32) + ) + + def predict(self) -> NDArray[np.float32]: + """预测下一帧位置(简单线性预测)。""" + predicted_bbox = self.bbox + self.velocity + # 确保边界框有效 + predicted_bbox[2] = max(predicted_bbox[2], predicted_bbox[0] + 1) + predicted_bbox[3] = max(predicted_bbox[3], predicted_bbox[1] + 1) + return predicted_bbox + + def update(self, detection: Detection2D) -> None: + """使用新检测更新轨迹。""" + new_bbox = detection.bbox.astype(np.float32) + # 更新速度(指数移动平均) + self.velocity = 0.7 * self.velocity + 0.3 * (new_bbox - self.bbox) + self.bbox = new_bbox + self.class_id = detection.class_id + self.confidence = detection.confidence + self.hits += 1 + self.time_since_update = 0 + + def mark_missed(self) -> None: + """标记为未匹配。""" + self.time_since_update += 1 + # 使用预测位置 + self.bbox = self.predict() + + def to_tracked_object(self) -> TrackedObject: + """转换为 TrackedObject。""" + return TrackedObject( + track_id=self.track_id, + bbox=self.bbox.copy(), + class_id=self.class_id, + confidence=self.confidence, + age=self.age, + hits=self.hits, + time_since_update=self.time_since_update, + ) + + +def _compute_iou(bbox1: NDArray[np.float32], bbox2: NDArray[np.float32]) -> float: + """计算两个边界框的 IoU。 + + Args: + bbox1: 边界框 1,[x1, y1, x2, y2] + bbox2: 边界框 2,[x1, y1, x2, y2] + + Returns: + IoU 值 + """ + # 计算交集 + x1 = max(bbox1[0], bbox2[0]) + y1 = max(bbox1[1], bbox2[1]) + x2 = min(bbox1[2], bbox2[2]) + y2 = min(bbox1[3], bbox2[3]) + + if x2 <= x1 or y2 <= y1: + return 0.0 + + intersection = (x2 - x1) * (y2 - y1) + + # 计算并集 + area1 = (bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1]) + area2 = (bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1]) + union = area1 + area2 - intersection + + if union <= 0: + return 0.0 + + return float(intersection / union) + + +def _compute_iou_matrix( + tracks: list[_Track], detections: list[Detection2D] +) -> NDArray[np.float32]: + """计算轨迹和检测之间的 IoU 矩阵。 + + Args: + tracks: 轨迹列表 + detections: 检测列表 + + Returns: + IoU 矩阵,shape (len(tracks), len(detections)) + """ + if not tracks or not detections: + return np.empty((len(tracks), len(detections)), dtype=np.float32) + + iou_matrix = np.zeros((len(tracks), len(detections)), dtype=np.float32) + + for i, track in enumerate(tracks): + for j, det in enumerate(detections): + iou_matrix[i, j] = _compute_iou(track.bbox, det.bbox) + + return iou_matrix + + +class MultiObjectTracker: + """多目标跟踪器。 + + 基于 IoU 匹配的简化版跟踪器,使用匈牙利算法进行检测-轨迹匹配。 + + 使用示例: + >>> tracker = MultiObjectTracker() + >>> for frame_id, detections in enumerate(all_detections): + ... tracked = tracker.update(detections, frame_id) + ... for obj in tracked: + ... print(f"Track {obj.track_id}: {obj.bbox}") + """ + + def __init__( + self, + max_age: int = 30, + min_hits: int = 3, + iou_threshold: float = 0.3, + config: Optional[TrackerConfig] = None, + ): + """初始化跟踪器。 + + Args: + max_age: 轨迹最大存活帧数(无匹配时) + min_hits: 轨迹确认所需最小匹配次数 + iou_threshold: IoU 匹配阈值 + config: 跟踪器配置(优先级高于单独参数) + """ + if config is not None: + self.config = config + else: + self.config = TrackerConfig( + max_age=max_age, + min_hits=min_hits, + iou_threshold=iou_threshold, + ) + + self._tracks: list[_Track] = [] + self._next_id: int = 1 + self._frame_count: int = 0 + + logger.info( + f"MultiObjectTracker 初始化: max_age={self.config.max_age}, " + f"min_hits={self.config.min_hits}, iou_threshold={self.config.iou_threshold}" + ) + + def update( + self, detections: list[Detection2D], frame_id: Optional[int] = None + ) -> list[TrackedObject]: + """更新跟踪器,返回跟踪结果。 + + Args: + detections: 当前帧的检测结果 + frame_id: 帧 ID(可选,用于日志) + + Returns: + 确认的跟踪目标列表 + """ + self._frame_count += 1 + current_frame = frame_id if frame_id is not None else self._frame_count + + logger.debug(f"帧 {current_frame}: 收到 {len(detections)} 个检测") + + # 增加所有轨迹的年龄 + for track in self._tracks: + track.age += 1 + + if not detections: + # 没有检测,标记所有轨迹为未匹配 + for track in self._tracks: + track.mark_missed() + self._remove_dead_tracks() + return self._get_confirmed_tracks() + + if not self._tracks: + # 没有轨迹,为所有检测创建新轨迹 + for det in detections: + self._create_track(det) + return self._get_confirmed_tracks() + + # 计算 IoU 矩阵 + iou_matrix = _compute_iou_matrix(self._tracks, detections) + + # 使用匈牙利算法进行匹配 + matched_tracks, matched_dets, unmatched_tracks, unmatched_dets = ( + self._hungarian_match(iou_matrix) + ) + + logger.debug( + f"匹配结果: {len(matched_tracks)} 匹配, " + f"{len(unmatched_tracks)} 未匹配轨迹, " + f"{len(unmatched_dets)} 未匹配检测" + ) + + # 更新匹配的轨迹 + for track_idx, det_idx in zip(matched_tracks, matched_dets): + self._tracks[track_idx].update(detections[det_idx]) + + # 标记未匹配的轨迹 + for track_idx in unmatched_tracks: + self._tracks[track_idx].mark_missed() + + # 为未匹配的检测创建新轨迹 + for det_idx in unmatched_dets: + self._create_track(detections[det_idx]) + + # 删除死亡轨迹 + self._remove_dead_tracks() + + return self._get_confirmed_tracks() + + def _hungarian_match( + self, iou_matrix: NDArray[np.float32] + ) -> tuple[list[int], list[int], list[int], list[int]]: + """使用匈牙利算法进行匹配。 + + Args: + iou_matrix: IoU 矩阵 + + Returns: + (匹配的轨迹索引, 匹配的检测索引, 未匹配的轨迹索引, 未匹配的检测索引) + """ + if iou_matrix.size == 0: + return [], [], list(range(len(self._tracks))), [] + + # 转换为代价矩阵(1 - IoU) + cost_matrix = 1 - iou_matrix + + # 匈牙利算法 + track_indices, det_indices = linear_sum_assignment(cost_matrix) + + matched_tracks: list[int] = [] + matched_dets: list[int] = [] + unmatched_tracks: list[int] = list(range(len(self._tracks))) + unmatched_dets: list[int] = list(range(iou_matrix.shape[1])) + + for track_idx, det_idx in zip(track_indices, det_indices): + # 检查 IoU 是否超过阈值 + if iou_matrix[track_idx, det_idx] >= self.config.iou_threshold: + matched_tracks.append(int(track_idx)) + matched_dets.append(int(det_idx)) + unmatched_tracks.remove(int(track_idx)) + unmatched_dets.remove(int(det_idx)) + + return matched_tracks, matched_dets, unmatched_tracks, unmatched_dets + + def _create_track(self, detection: Detection2D) -> _Track: + """创建新轨迹。""" + track = _Track( + track_id=self._next_id, + bbox=detection.bbox.astype(np.float32), + class_id=detection.class_id, + confidence=detection.confidence, + ) + self._tracks.append(track) + self._next_id += 1 + + logger.debug(f"创建新轨迹 ID={track.track_id}") + return track + + def _remove_dead_tracks(self) -> None: + """删除死亡轨迹。""" + before_count = len(self._tracks) + self._tracks = [ + t for t in self._tracks if t.time_since_update < self.config.max_age + ] + removed = before_count - len(self._tracks) + if removed > 0: + logger.debug(f"删除 {removed} 条死亡轨迹") + + def _get_confirmed_tracks(self) -> list[TrackedObject]: + """获取确认的轨迹(hits >= min_hits)。""" + confirmed = [ + t.to_tracked_object() + for t in self._tracks + if t.hits >= self.config.min_hits and t.time_since_update == 0 + ] + logger.debug(f"返回 {len(confirmed)} 条确认轨迹") + return confirmed + + def reset(self) -> None: + """重置跟踪器状态。""" + self._tracks.clear() + self._next_id = 1 + self._frame_count = 0 + logger.info("跟踪器已重置") + + def get_all_tracks(self) -> list[TrackedObject]: + """获取所有轨迹(包括未确认的)。""" + return [t.to_tracked_object() for t in self._tracks] + + @property + def track_count(self) -> int: + """当前轨迹数量。""" + return len(self._tracks) + + @property + def confirmed_track_count(self) -> int: + """确认轨迹数量。""" + return len([t for t in self._tracks if t.hits >= self.config.min_hits]) diff --git a/src/aylm/tools/obstacle_marker.py b/src/aylm/tools/obstacle_marker.py index 4e72cc6..251b4de 100644 --- a/src/aylm/tools/obstacle_marker.py +++ b/src/aylm/tools/obstacle_marker.py @@ -74,6 +74,11 @@ class ObstacleBox3D: label: SemanticLabel # 语义标签 confidence: float # 平均置信度 point_indices: NDArray[np.int64] = field(repr=False) # 属于该障碍物的点索引 + track_id: Optional[int] = None # 跟踪 ID(跨帧关联) + frame_id: Optional[int] = None # 帧 ID(时序关联) + timestamp: Optional[float] = None # 时间戳(秒) + velocity: Optional[tuple[float, float, float]] = None # 速度向量 (vx, vy, vz) m/s + motion_vector: Optional[tuple[float, float, float]] = None # 运动矢量(帧间位移) @property def is_movable(self) -> bool: @@ -140,7 +145,7 @@ def to_dict(self) -> dict: - center_cv: OpenCV 坐标系 (X右,Y下,Z前) - center_robot: 机器人坐标系 (X前,Y左,Z上) """ - return { + result = { "type": "可运动障碍物" if self.is_movable else "静态障碍物", "category": LABEL_DESCRIPTIONS.get(self.label, "未知"), "movable": self.is_movable, @@ -155,6 +160,24 @@ def to_dict(self) -> dict: "_label_id": int(self.label.value), } + # 添加跟踪和时序信息 + if self.track_id is not None: + result["track_id"] = self.track_id + if self.frame_id is not None: + result["frame_id"] = self.frame_id + if self.timestamp is not None: + result["timestamp"] = self.timestamp + + # 添加运动信息 + if self.velocity is not None: + result["velocity_cv"] = list(self.velocity) + result["velocity_robot"] = list(self._cv_to_robot(self.velocity)) + if self.motion_vector is not None: + result["motion_vector_cv"] = list(self.motion_vector) + result["motion_vector_robot"] = list(self._cv_to_robot(self.motion_vector)) + + return result + def get_box_vertices(self) -> NDArray[np.float64]: """获取边界框的8个顶点,用于可视化。""" min_c = self.min_corner @@ -419,18 +442,29 @@ def highlight_obstacles( return colors - def export_to_json(self, obstacles: list[ObstacleBox3D], output_path: Path) -> None: + def export_to_json( + self, + obstacles: list[ObstacleBox3D], + output_path: Path, + frame_id: Optional[int] = None, + timestamp: Optional[float] = None, + ) -> None: """ 导出障碍物列表为 JSON 格式(用于导航系统)。 Args: obstacles: 障碍物列表 output_path: 输出文件路径 + frame_id: 帧 ID(可选,用于时序关联) + timestamp: 时间戳(可选,秒) """ # 统计可运动和静态障碍物数量 movable_count = sum(1 for obs in obstacles if obs.is_movable) static_count = len(obstacles) - movable_count + # 统计有跟踪 ID 的障碍物 + tracked_count = sum(1 for obs in obstacles if obs.track_id is not None) + data = { "coordinate_systems": { "cv": { @@ -446,9 +480,16 @@ def export_to_json(self, obstacles: list[ObstacleBox3D], output_path: Path) -> N "total_count": len(obstacles), "movable_count": movable_count, "static_count": static_count, + "tracked_count": tracked_count, "obstacles": [obs.to_dict() for obs in obstacles], } + # 添加时序元数据 + if frame_id is not None: + data["frame_id"] = frame_id + if timestamp is not None: + data["timestamp"] = timestamp + with open(output_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) diff --git a/src/aylm/tools/pipeline_processor.py b/src/aylm/tools/pipeline_processor.py index 4fa765e..5131e89 100644 --- a/src/aylm/tools/pipeline_processor.py +++ b/src/aylm/tools/pipeline_processor.py @@ -5,6 +5,7 @@ from __future__ import annotations +import contextlib import gc import logging import threading @@ -13,10 +14,16 @@ from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Callable +from typing import TYPE_CHECKING, Callable, ClassVar + +if TYPE_CHECKING: + from torch.nn import Module + + from aylm.tools.object_detector import ObjectDetector + from aylm.tools.pointcloud_voxelizer import PointCloudVoxelizer import torch -import torch.nn.functional as F +from torch.nn import functional as functional_nn # 模块级常量 DEFAULT_VOXEL_SIZE = 0.05 # 5cm 体素 @@ -74,11 +81,14 @@ class PipelineConfig: async_mode: bool = False # 切片配置 enable_slice: bool = True # 是否启用切片 - slice_radius: float = 20.0 # 切片半径(米) + slice_radius: float = 10.0 # 切片半径(米) # 语义检测配置 enable_semantic: bool = True # 是否启用语义检测(默认开启) + # 输入分辨率配置 + # 注意:SHARP 模型要求固定 1536,因为内部金字塔结构依赖 1536→768→384 + internal_resolution: int = 1536 # 内部处理分辨率(固定值,不可更改) semantic_model: str = "yolo11n-seg.pt" # YOLO 模型 - semantic_confidence: float = 0.5 # 检测置信度 + semantic_confidence: float = 0.25 # 检测置信度 colorize_semantic: bool = True # 语义着色 # 导航输出配置 output_navigation_ply: bool = True # 是否输出导航用点云(机器人坐标系) @@ -122,7 +132,7 @@ def avg_voxel_time(self) -> float: class PipelineLogger: """流水线日志记录器。""" - LEVEL_PREFIX = { + LEVEL_PREFIX: ClassVar[dict[str, str]] = { "INFO": " ", "STAGE": ">>>", "OK": " ✓ ", @@ -131,7 +141,7 @@ class PipelineLogger: "PROGRESS": " → ", } - STATUS_DISPLAY = { + STATUS_DISPLAY: ClassVar[dict[TaskStatus, str]] = { TaskStatus.PENDING: "⏳ 等待中", TaskStatus.PREDICTING: "🔄 推理中", TaskStatus.PREDICTED: "📦 待体素化", @@ -224,11 +234,11 @@ def __init__(self, config: PipelineConfig | None = None): self.log = PipelineLogger(self.config.verbose) self.stats = PipelineStats() - self._predictor = None + self._predictor: Module | None = None self._device: torch.device | None = None self._model_loaded = False - self._voxelizer = None - self._detector = None # YOLO 语义检测器 + self._voxelizer: PointCloudVoxelizer | None = None + self._detector: ObjectDetector | None = None # YOLO 语义检测器 self._tasks: list[ImageTask] = [] self._stop_event = threading.Event() self._predict_lock = threading.Lock() @@ -238,7 +248,7 @@ def __init__(self, config: PipelineConfig | None = None): def __enter__(self): return self - def __exit__(self, exc_type, exc_val, exc_tb): + def __exit__(self, _exc_type, _exc_val, _exc_tb): self.cleanup() return False @@ -260,10 +270,8 @@ def _unload_model(self): try: if self._predictor is not None: if self._device and self._device.type != "cpu": - try: + with contextlib.suppress(Exception): self._predictor.cpu() - except Exception: - pass del self._predictor self._predictor = None @@ -276,10 +284,8 @@ def _unload_model(self): torch.cuda.synchronize() if hasattr(torch, "mps") and hasattr(torch.mps, "empty_cache"): - try: + with contextlib.suppress(Exception): torch.mps.empty_cache() - except Exception: - pass self.log.ok("模型已卸载,内存已释放") except Exception as e: @@ -323,7 +329,7 @@ def _detect_device(self) -> torch.device: return torch.device(self.config.device) if torch.cuda.is_available(): return torch.device("cuda") - if hasattr(torch, "mps") and torch.mps.is_available(): + if hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): return torch.device("mps") return torch.device("cpu") @@ -353,9 +359,10 @@ def _load_model(self) -> bool: ) self._predictor = create_predictor(PredictorParams()) - self._predictor.load_state_dict(state_dict) - self._predictor.eval() - self._predictor.to(self._device) + predictor = self._predictor # 局部变量,mypy 可以推断非 None + predictor.load_state_dict(state_dict) + predictor.eval() + predictor.to(self._device) self._model_loaded = True self.log.ok("模型加载完成") @@ -399,15 +406,17 @@ def _predict_single(self, task: ImageTask, output_dir: Path) -> bool: self.log.info(f" 图像尺寸: {width}x{height}, 焦距: {f_px:.1f}px") - # 预处理 - internal_shape = (1536, 1536) + # 预处理(使用配置的内部分辨率) + res = self.config.internal_resolution + internal_shape = (res, res) + self.log.info(f" 内部处理分辨率: {res}x{res}") image_pt = ( torch.from_numpy(image.copy()).float().to(self._device).permute(2, 0, 1) / 255.0 ) disparity_factor = torch.tensor([f_px / width]).float().to(self._device) - image_resized_pt = F.interpolate( + image_resized_pt = functional_nn.interpolate( image_pt[None], size=(internal_shape[1], internal_shape[0]), mode="bilinear", @@ -416,6 +425,7 @@ def _predict_single(self, task: ImageTask, output_dir: Path) -> bool: # 推理(需要锁保护,因为模型不是线程安全的) with self._predict_lock: + assert self._predictor is not None, "模型未加载" gaussians_ndc = self._predictor(image_resized_pt, disparity_factor) # 后处理 @@ -475,26 +485,31 @@ def _predict_single(self, task: ImageTask, output_dir: Path) -> bool: def _voxelize_single( self, task: ImageTask, output_dir: Path, navigation_dir: Path | None = None - ) -> bool: - """对单个PLY文件进行切片、体素化,可选语义融合。 + ) -> Path | None: + """对单个PLY文件进行切片、体素化。 - 处理流程:SHARP输出 → 切片 → 体素化 → 语义融合 + 处理流程:SHARP输出 → 切片 → 体素化 + 语义融合在外部异步执行。 Args: task: 图像任务 output_dir: 体素化输出目录 - navigation_dir: 导航用点云输出目录(机器人坐标系) + navigation_dir: 导航用点云输出目录(未使用,保留兼容性) + + Returns: + 体素化后的 PLY 文件路径,失败返回 None """ if task.status != TaskStatus.PREDICTED or task.ply_output_path is None: - return False + return None task.status = TaskStatus.VOXELIZING task.voxel_start_time = time.time() - self.log.progress(f"[{task.index+1}] 开始处理: {task.ply_output_path.name}") + self.log.progress(f"[{task.index+1}] 开始体素化: {task.ply_output_path.name}") try: # 确定输入文件(可能经过切片) + assert task.ply_output_path is not None, "PLY 输出路径未设置" input_ply_path = task.ply_output_path # 步骤1:切片(如果启用) @@ -504,6 +519,7 @@ def _voxelize_single( input_ply_path = sliced_path # 步骤2:体素化 + assert self._voxelizer is not None, "体素化器未初始化" output_path = output_dir / f"vox_{task.ply_output_path.name}" self._voxelizer.process( input_ply_path, @@ -514,35 +530,48 @@ def _voxelize_single( # 清理临时切片文件 if self.config.enable_slice and input_ply_path != task.ply_output_path: - try: + with contextlib.suppress(Exception): input_ply_path.unlink() - except Exception: - pass - - # 步骤3:语义融合(如果启用) - if self.config.enable_semantic and self._detector is not None: - self._apply_semantic_fusion(task, output_path, navigation_dir) task.voxel_output_path = output_path - task.status = TaskStatus.COMPLETED - task.voxel_end_time = time.time() - - voxel_time = task.voxel_end_time - task.voxel_start_time + voxel_time = time.time() - task.voxel_start_time self.log.ok( - f"[{task.index+1}] 处理完成: {task.ply_output_path.name} ({voxel_time:.2f}s)" + f"[{task.index+1}] 体素化完成: {output_path.name} ({voxel_time:.2f}s)" ) - self.log.info(f" 输出: {output_path.name}") - return True + return output_path except Exception as e: task.status = TaskStatus.FAILED task.error_message = str(e) task.voxel_end_time = time.time() - self.log.error(f"[{task.index+1}] 处理失败: {task.ply_output_path.name}") - self.log.error(f" 错误类型: {type(e).__name__}") - self.log.error(f" 错误信息: {e}") - logger.exception(f"处理异常详情 - {task.ply_output_path.name}") + self.log.error(f"[{task.index+1}] 体素化失败: {task.ply_output_path.name}") + self.log.error(f" 错误: {e}") + logger.exception(f"体素化异常 - {task.ply_output_path.name}") + return None + + def _semantic_and_navigation( + self, task: ImageTask, voxel_path: Path, navigation_dir: Path | None = None + ) -> bool: + """执行语义检测和导航输出(可异步调用)。 + + Args: + task: 图像任务 + voxel_path: 体素化后的 PLY 文件路径 + navigation_dir: 导航输出目录 + + Returns: + 是否成功 + """ + try: + self._apply_semantic_fusion(task, voxel_path, navigation_dir) + task.status = TaskStatus.COMPLETED + task.voxel_end_time = time.time() + return True + except Exception as e: + self.log.error(f"[{task.index+1}] 语义处理失败: {e}") + task.status = TaskStatus.COMPLETED # 体素化成功,语义失败不算整体失败 + task.voxel_end_time = time.time() return False def _apply_slice(self, task: ImageTask) -> Path | None: @@ -556,6 +585,10 @@ def _apply_slice(self, task: ImageTask) -> Path | None: """ import numpy as np + if task.ply_output_path is None: + self.log.warn(" PLY 输出路径未设置,跳过切片") + return None + try: from plyfile import PlyData, PlyElement except ImportError: @@ -646,6 +679,7 @@ def _apply_semantic_fusion( height, width = image.shape[:2] # 执行目标检测 + assert self._detector is not None, "检测器未初始化" detections = self._detector.detect(image, return_masks=True) self.log.info(f" 检测到 {len(detections)} 个目标") @@ -802,7 +836,7 @@ def _collect_images(self, input_path: Path) -> list[Path]: if input_path.is_file(): return [input_path] if input_path.suffix.lower() in extensions else [] - images = [] + images: list[Path] = [] for ext in extensions: images.extend(input_path.glob(f"*{ext}")) images.extend(input_path.glob(f"*{ext.upper()}")) @@ -1019,10 +1053,10 @@ def _execute_pipeline( ): """执行流水线处理逻辑。 - 流水线策略: - 1. 第一张图片: 只做推理(无并行) - 2. 第2到N张图片: 推理第N张 || 体素化第N-1张(并行) - 3. 最后: 体素化最后一张图片(无并行) + 三级并行流水线: + - 推理(N): 主线程执行 SHARP 推理 + - 体素化(N-1): 线程1 执行体素化 + - 语义检测(N-2): 线程2 执行语义检测和导航输出 时间线示意: 图片1: [====推理====] @@ -1030,8 +1064,11 @@ def _execute_pipeline( 图片1: [====体素化====] 图片3: [====推理====] 图片2: [====体素化====] + 图片1: [====语义====] ... + 每帧完成语义检测后立即输出导航点云,实现快速响应。 + Args: output_dir: PLY 输出目录 voxel_output_dir: 体素化输出目录 @@ -1043,57 +1080,117 @@ def _execute_pipeline( self.log.warn("没有任务需要处理") return - # 使用线程池执行体素化(推理在主线程,因为GPU操作需要同步) - with ThreadPoolExecutor(max_workers=1) as voxel_executor: + # 使用两个线程池:体素化和语义检测 + with ( + ThreadPoolExecutor( + max_workers=1, thread_name_prefix="voxel" + ) as voxel_executor, + ThreadPoolExecutor( + max_workers=1, thread_name_prefix="semantic" + ) as semantic_executor, + ): + voxel_future: Future | None = None - prev_task_for_voxel: ImageTask | None = None + semantic_future: Future | None = None + current_voxel_task: ImageTask | None = None # 当前正在体素化的任务 + + # 待处理队列 + pending_voxel_task: ImageTask | None = None # 等待体素化的任务 + pending_semantic: tuple[ImageTask, Path] | None = None # 等待语义检测 for i, task in enumerate(self._tasks): self.log.info(f"\n{'─' * 40}") self.log.info(f"处理进度: {i+1}/{total}") - # 显示当前阶段的并行状态 - if i == 0: - self.log.info(" 阶段: 推理第1张(无并行)") - elif i < total: - self.log.info(f" 阶段: 推理第{i+1}张 || 体素化第{i}张(并行)") + # 显示当前并行状态 + parallel_info = [] + if pending_voxel_task is not None: + parallel_info.append(f"体素化第{pending_voxel_task.index+1}张") + if pending_semantic is not None and self.config.enable_semantic: + parallel_info.append(f"语义第{pending_semantic[0].index+1}张") + if parallel_info: + self.log.info( + f" 并行: 推理第{i+1}张 || {' || '.join(parallel_info)}" + ) + else: + self.log.info(f" 阶段: 推理第{i+1}张") + + # 启动语义检测(如果有待处理的) + if pending_semantic is not None and self.config.enable_semantic: + sem_task, sem_voxel_path = pending_semantic + self.log.progress( + f" 启动语义检测: [{sem_task.index+1}] {sem_task.image_path.name}" + ) + semantic_future = semantic_executor.submit( + self._semantic_and_navigation, + sem_task, + sem_voxel_path, + navigation_dir, + ) + pending_semantic = None - # 如果有上一张图片需要体素化,启动异步体素化 - if prev_task_for_voxel is not None: + # 启动体素化(如果有待处理的) + if pending_voxel_task is not None: + current_voxel_task = pending_voxel_task self.log.progress( - f" 启动并行体素化: [{prev_task_for_voxel.index+1}] {prev_task_for_voxel.image_path.name}" + f" 启动体素化: [{current_voxel_task.index+1}] {current_voxel_task.image_path.name}" ) voxel_future = voxel_executor.submit( self._voxelize_single, - prev_task_for_voxel, + current_voxel_task, voxel_output_dir, navigation_dir, ) + pending_voxel_task = None # 执行当前图片的推理(主线程) predict_success = self._predict_single(task, output_dir) - # 等待并行的体素化完成(如果有) + # 等待体素化完成,获取结果用于语义检测 if voxel_future is not None: try: - voxel_future.result() + voxel_path = voxel_future.result() + if voxel_path is not None and current_voxel_task is not None: + # 将体素化结果加入语义检测队列 + pending_semantic = (current_voxel_task, voxel_path) except Exception as e: self.log.error(f"体素化任务异常: {e}") voxel_future = None + current_voxel_task = None # 记录当前任务用于下一轮的体素化 if predict_success: - prev_task_for_voxel = task - else: - prev_task_for_voxel = None + pending_voxel_task = task - # 处理最后一张图片的体素化(同步执行,无并行) - if prev_task_for_voxel is not None: + # 处理剩余的体素化任务 + if pending_voxel_task is not None: self.log.info(f"\n{'─' * 40}") - self.log.info("最终阶段: 体素化最后一张图片") - self._voxelize_single( - prev_task_for_voxel, voxel_output_dir, navigation_dir + self.log.info("收尾阶段: 处理剩余任务") + + self.log.progress( + f" 体素化: [{pending_voxel_task.index+1}] {pending_voxel_task.image_path.name}" + ) + voxel_path = self._voxelize_single( + pending_voxel_task, voxel_output_dir, navigation_dir + ) + + if voxel_path is not None: + pending_semantic = (pending_voxel_task, voxel_path) + + # 等待最后的语义检测完成 + if semantic_future is not None: + try: + semantic_future.result() + except Exception as e: + self.log.error(f"语义检测任务异常: {e}") + + # 处理最后一个语义检测 + if pending_semantic is not None and self.config.enable_semantic: + sem_task, sem_voxel_path = pending_semantic + self.log.progress( + f" 语义检测: [{sem_task.index+1}] {sem_task.image_path.name}" ) + self._semantic_and_navigation(sem_task, sem_voxel_path, navigation_dir) def run_pipeline( diff --git a/src/aylm/tools/pointcloud_voxelizer.py b/src/aylm/tools/pointcloud_voxelizer.py index aa94351..7c47fac 100644 --- a/src/aylm/tools/pointcloud_voxelizer.py +++ b/src/aylm/tools/pointcloud_voxelizer.py @@ -79,6 +79,62 @@ def filter_by_mask(self, mask: "NDArray[np.bool_]") -> "PointCloud": colors = self.colors[mask] if self.colors is not None else None return PointCloud(points=self.points[mask], colors=colors) + @classmethod + def from_ply(cls, path: "Path") -> "PointCloud": + """从 PLY 文件加载点云。""" + ply = PlyData.read(str(path)) + vertex = ply["vertex"] + + points = np.column_stack([vertex["x"], vertex["y"], vertex["z"]]) + + colors = None + if "red" in vertex.data.dtype.names: + colors = np.column_stack( + [vertex["red"], vertex["green"], vertex["blue"]] + ).astype(np.float64) + if colors.max() > 1.0: + colors = colors / 255.0 + + return cls(points=points.astype(np.float64), colors=colors) + + def to_ply(self, path: "Path") -> None: + """保存点云到 PLY 文件。""" + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + + n = len(self.points) + if self.colors is not None: + colors = self.colors + if colors.max() <= 1.0: + colors = (colors * 255).astype(np.uint8) + else: + colors = colors.astype(np.uint8) + + dtype = [ + ("x", "f4"), + ("y", "f4"), + ("z", "f4"), + ("red", "u1"), + ("green", "u1"), + ("blue", "u1"), + ] + data = np.zeros(n, dtype=dtype) + data["x"] = self.points[:, 0] + data["y"] = self.points[:, 1] + data["z"] = self.points[:, 2] + data["red"] = colors[:, 0] + data["green"] = colors[:, 1] + data["blue"] = colors[:, 2] + else: + dtype = [("x", "f4"), ("y", "f4"), ("z", "f4")] + data = np.zeros(n, dtype=dtype) + data["x"] = self.points[:, 0] + data["y"] = self.points[:, 1] + data["z"] = self.points[:, 2] + + vertex = PlyElement.describe(data, "vertex") + PlyData([vertex], text=False).write(str(path)) + class PointCloudVoxelizer: """点云体素化处理器。""" @@ -100,9 +156,12 @@ def _get_device(self) -> str: return GPU_DEVICE elif device_config == "cuda" and torch.cuda.is_available(): return "cuda" - elif device_config == "mps" and hasattr(torch.backends, "mps"): - if torch.backends.mps.is_available(): - return "mps" + elif ( + device_config == "mps" + and hasattr(torch.backends, "mps") + and torch.backends.mps.is_available() + ): + return "mps" return "cpu" def _should_use_gpu(self) -> bool: @@ -169,7 +228,7 @@ def _remove_outliers_o3d(self, pc: PointCloud, cfg: VoxelizerConfig) -> PointClo """使用Open3D去除离群点。""" pcd = self._to_o3d(pc) - pcd_clean, indices = pcd.remove_statistical_outlier( + _pcd_clean, indices = pcd.remove_statistical_outlier( nb_neighbors=cfg.statistical_nb_neighbors, std_ratio=cfg.statistical_std_ratio, ) @@ -514,7 +573,7 @@ def _detect_ground_gpu( # 找最佳平面 best_idx = inliers_count.argmax() if inliers_count[best_idx] > best_inlier_count: - best_inlier_count = inliers_count[best_idx].item() + best_inlier_count = int(inliers_count[best_idx].item()) best_inliers_mask = ( distances[:, best_idx] < cfg.ransac_distance_threshold ) @@ -562,12 +621,12 @@ def _voxel_downsample_numpy(self, pc: PointCloud) -> PointCloud: for i, key in enumerate(map(tuple, voxel_indices)): voxel_dict.setdefault(key, []).append(i) - new_points = [] - new_colors = [] if pc.colors is not None else None + new_points: list[np.ndarray] = [] + new_colors: list[np.ndarray] | None = [] if pc.colors is not None else None for indices in voxel_dict.values(): new_points.append(pc.points[indices].mean(axis=0)) - if pc.colors is not None: + if pc.colors is not None and new_colors is not None: new_colors.append(pc.colors[indices].mean(axis=0)) points = np.array(new_points) diff --git a/src/aylm/tools/semantic_fusion.py b/src/aylm/tools/semantic_fusion.py index f3af4fc..adf74ce 100644 --- a/src/aylm/tools/semantic_fusion.py +++ b/src/aylm/tools/semantic_fusion.py @@ -334,7 +334,7 @@ def save_navigation_ply( colors = semantic_pc.colorize_by_semantic() # 体素化:基于密度阈值生成实体方块信息 - voxel_centers, voxel_colors, voxel_labels, _, _ = self._create_solid_voxels( + voxel_centers, voxel_colors, _voxel_labels, _, _ = self._create_solid_voxels( robot_points, colors, semantic_pc.labels, @@ -499,7 +499,8 @@ def _create_solid_voxels( # 使用 defaultdict 聚合每个体素的点索引 voxel_dict: dict[tuple[int, int, int], list[int]] = defaultdict(list) for i, idx in enumerate(voxel_indices): - voxel_dict[tuple(int(x) for x in idx)].append(i) + key: tuple[int, int, int] = (int(idx[0]), int(idx[1]), int(idx[2])) + voxel_dict[key].append(i) # 过滤:只保留点数达到阈值的体素 solid_voxels = {k: v for k, v in voxel_dict.items() if len(v) >= min_points} diff --git a/src/aylm/tools/semantic_types.py b/src/aylm/tools/semantic_types.py index f706963..8b6c669 100644 --- a/src/aylm/tools/semantic_types.py +++ b/src/aylm/tools/semantic_types.py @@ -61,6 +61,8 @@ class Detection2D: class_id: int # COCO 类别 ID confidence: float # 置信度 semantic_label: SemanticLabel # 语义标签 + track_id: Optional[int] = None # 跟踪 ID(跨帧关联) + frame_id: Optional[int] = None # 帧 ID(时序关联) @property def area(self) -> float: @@ -116,11 +118,11 @@ class CameraIntrinsics: cy: float # 主点 y 坐标(像素) @classmethod - def from_matrix(cls, K: NDArray[np.float64]) -> "CameraIntrinsics": + def from_matrix(cls, k: NDArray[np.float64]) -> "CameraIntrinsics": """从 3x3 内参矩阵创建。 Args: - K: 3x3 相机内参矩阵 + k: 3x3 相机内参矩阵 [[fx, 0, cx], [ 0, fy, cy], [ 0, 0, 1]] @@ -128,7 +130,7 @@ def from_matrix(cls, K: NDArray[np.float64]) -> "CameraIntrinsics": Returns: CameraIntrinsics 实例 """ - return cls(fx=K[0, 0], fy=K[1, 1], cx=K[0, 2], cy=K[1, 2]) + return cls(fx=k[0, 0], fy=k[1, 1], cx=k[0, 2], cy=k[1, 2]) @classmethod def from_focal_length( diff --git a/src/aylm/tools/video_config.py b/src/aylm/tools/video_config.py index f08218d..af89399 100644 --- a/src/aylm/tools/video_config.py +++ b/src/aylm/tools/video_config.py @@ -5,6 +5,7 @@ import logging from dataclasses import asdict +from enum import Enum from pathlib import Path from typing import Any @@ -50,7 +51,7 @@ def _validate_range(value: Any, min_val: float, max_val: float, name: str) -> No ) -def _validate_enum(value: str, enum_class: type, name: str) -> None: +def _validate_enum(value: str, enum_class: type[Enum], name: str) -> None: """验证枚举值。""" valid_values = [e.value for e in enum_class] if value not in valid_values: diff --git a/src/aylm/tools/video_extractor.py b/src/aylm/tools/video_extractor.py index 30c2114..9b1e8da 100644 --- a/src/aylm/tools/video_extractor.py +++ b/src/aylm/tools/video_extractor.py @@ -9,10 +9,9 @@ import time from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path -from typing import TYPE_CHECKING, Callable +from typing import Any, Callable import cv2 -import numpy as np from .video_types import ( FrameExtractionMethod, @@ -22,9 +21,6 @@ VideoMetadata, ) -if TYPE_CHECKING: - from numpy.typing import NDArray - logger = logging.getLogger(__name__) ProgressCallback = Callable[[int, int, float], None] @@ -73,31 +69,36 @@ def _calculate_frame_indices( fps, total_frames = metadata.fps, metadata.total_frames # 计算帧间隔 + interval_float: float if method == FrameExtractionMethod.INTERVAL: - interval = max(1, int(self.config.frame_interval * fps)) + interval_float = float(max(1, int(self.config.frame_interval * fps))) elif ( method == FrameExtractionMethod.UNIFORM and self.config.target_fps and self.config.target_fps < fps ): - interval = fps / self.config.target_fps + interval_float = fps / self.config.target_fps else: - interval = max(1, int(fps)) # KEYFRAME 和 SCENE_CHANGE 默认每秒一帧 + interval_float = float( + max(1, int(fps)) + ) # KEYFRAME 和 SCENE_CHANGE 默认每秒一帧 # 生成帧索引 if method == FrameExtractionMethod.UNIFORM and ( not self.config.target_fps or self.config.target_fps >= fps ): indices = [(i, i / fps) for i in range(total_frames)] - elif isinstance(interval, float): + elif interval_float != int(interval_float): + # 非整数间隔,使用浮点累加 indices = [] i = 0.0 while int(i) < total_frames: frame_idx = int(i) indices.append((frame_idx, frame_idx / fps)) - i += interval + i += interval_float else: - indices = [(i, i / fps) for i in range(0, total_frames, interval)] + interval_int = int(interval_float) + indices = [(i, i / fps) for i in range(0, total_frames, interval_int)] # 限制最大帧数 if self.config.max_frames: @@ -105,7 +106,7 @@ def _calculate_frame_indices( return indices - def compress_frame(self, frame: NDArray[np.uint8]) -> NDArray[np.uint8]: + def compress_frame(self, frame: Any) -> Any: """压缩帧图像(调整大小)。""" if self.config.resize_width is None and self.config.resize_height is None: return frame @@ -153,13 +154,13 @@ def extract_frame( try: cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index) ret, frame = cap.read() - if not ret: + if not ret or frame is None: logger.warning(f"Cannot read frame {frame_index} from {video_path}") return None - frame = self.compress_frame(frame) + compressed = self.compress_frame(frame) output_path.parent.mkdir(parents=True, exist_ok=True) - cv2.imwrite(str(output_path), frame, self._get_image_write_params()) + cv2.imwrite(str(output_path), compressed, self._get_image_write_params()) fps = cap.get(cv2.CAP_PROP_FPS) return FrameInfo( @@ -190,13 +191,13 @@ def _extract_frames_batch( for frame_index, timestamp in frame_indices: cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index) ret, frame = cap.read() - if not ret: + if not ret or frame is None: logger.warning(f"Cannot read frame {frame_index} from {video_path}") continue - frame = self.compress_frame(frame) + compressed = self.compress_frame(frame) output_path = output_dir / f"frame_{frame_index:06d}.{output_format}" - cv2.imwrite(str(output_path), frame, params) + cv2.imwrite(str(output_path), compressed, params) frames.append( FrameInfo( @@ -233,7 +234,7 @@ def extract_frames( start_time = time.time() metadata, output_dir, error = self._prepare_extraction(video_path, output_dir) - if error: + if error or metadata is None: return FrameExtractionResult( video_path=video_path, output_dir=output_dir, error_message=error ) @@ -270,7 +271,7 @@ def extract_frames_async( start_time = time.time() metadata, output_dir, error = self._prepare_extraction(video_path, output_dir) - if error: + if error or metadata is None: return FrameExtractionResult( video_path=video_path, output_dir=output_dir, error_message=error ) diff --git a/src/aylm/tools/video_pipeline.py b/src/aylm/tools/video_pipeline.py index 8caa0c6..c85f4f9 100644 --- a/src/aylm/tools/video_pipeline.py +++ b/src/aylm/tools/video_pipeline.py @@ -1,9 +1,11 @@ """视频处理流水线模块。 -实现帧提取与处理的并行流水线:帧提取、SHARP推理、体素化、语义融合。 +实现帧提取与处理的并行流水线:帧提取、SHARP推理、体素化、语义融合、目标跟踪。 """ +import contextlib import gc +import json import logging import queue import threading @@ -11,9 +13,13 @@ from collections.abc import Callable from dataclasses import dataclass from pathlib import Path +from typing import Optional +import numpy as np import torch +from .motion_estimator import MotionEstimator +from .object_tracker import MultiObjectTracker, TrackedObject, TrackerConfig from .pointcloud_voxelizer import PointCloudVoxelizer, VoxelizerConfig from .video_config import load_or_create_config from .video_extractor import VideoExtractor @@ -48,10 +54,24 @@ class VideoPipelineConfig: # 语义检测配置 enable_semantic: bool = True # 是否启用语义检测 semantic_model: str = "yolo11n-seg.pt" # YOLO 模型 - semantic_confidence: float = 0.5 # 检测置信度阈值 + semantic_confidence: float = 0.25 # 检测置信度阈值 colorize_semantic: bool = True # 是否根据语义标签着色 + # 点云切片配置 + enable_slice: bool = True # 是否启用点云切片 + slice_radius: float = 10.0 # 切片半径(米) # 导航输出配置 output_navigation_ply: bool = True # 是否输出导航用点云(机器人坐标系) + # 输入分辨率配置 + # 注意:SHARP 模型要求固定 1536,因为内部金字塔结构依赖 1536→768→384 + internal_resolution: int = 1536 # 内部处理分辨率(固定值,不可更改) + # 目标跟踪配置 + enable_tracking: bool = True # 是否启用目标跟踪 + tracker_max_age: int = 30 # 轨迹最大存活帧数 + tracker_min_hits: int = 3 # 轨迹确认所需最小匹配次数 + tracker_iou_threshold: float = 0.3 # IoU 匹配阈值 + # 运动估计配置 + motion_fps: float = 30.0 # 帧率(用于运动估计) + motion_stationary_threshold: float = 0.1 # 静止判定阈值(m/s) class VideoPipelineProcessor: @@ -73,6 +93,8 @@ def __init__(self, config: VideoPipelineConfig | None = None): self._model_loaded = False self._voxelizer: PointCloudVoxelizer | None = None self._detector = None # YOLO 语义检测器 + self._tracker: Optional[MultiObjectTracker] = None # 目标跟踪器 + self._motion_estimator: Optional[MotionEstimator] = None # 运动估计器 self._frame_queue: queue.Queue[FrameInfo] = queue.Queue( maxsize=self.config.frame_queue_size ) @@ -85,7 +107,7 @@ def _detect_device(self) -> torch.device: return torch.device(self.config.device) if torch.cuda.is_available(): return torch.device("cuda") - if hasattr(torch, "mps") and torch.mps.is_available(): + if hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): return torch.device("mps") return torch.device("cpu") @@ -116,10 +138,11 @@ def _load_model(self) -> bool: model_url, progress=True, map_location=self._device ) - self._predictor = create_predictor(PredictorParams()) - self._predictor.load_state_dict(state_dict) - self._predictor.eval() - self._predictor.to(self._device) + predictor = create_predictor(PredictorParams()) + predictor.load_state_dict(state_dict) + predictor.eval() + predictor.to(self._device) + self._predictor = predictor self._model_loaded = True logger.info("Model loaded successfully") @@ -136,10 +159,8 @@ def _unload_model(self): logger.info("Unloading model...") if self._predictor is not None: if self._device and self._device.type != "cpu": - try: + with contextlib.suppress(Exception): self._predictor.cpu() - except Exception: - pass del self._predictor self._predictor = None self._device = None @@ -180,6 +201,96 @@ def _cleanup_detector(self): del self._detector self._detector = None + def _load_tracker(self): + """加载目标跟踪器和运动估计器。""" + logger.info("Initializing object tracker and motion estimator...") + tracker_config = TrackerConfig( + max_age=self.config.tracker_max_age, + min_hits=self.config.tracker_min_hits, + iou_threshold=self.config.tracker_iou_threshold, + ) + self._tracker = MultiObjectTracker(config=tracker_config) + self._motion_estimator = MotionEstimator( + fps=self.config.motion_fps, + stationary_threshold=self.config.motion_stationary_threshold, + ) + logger.info( + f"Tracker initialized (max_age={self.config.tracker_max_age}, " + f"min_hits={self.config.tracker_min_hits})" + ) + + def _cleanup_tracker(self): + """清理跟踪器和运动估计器。""" + if self._tracker is not None: + logger.info("Cleaning up tracker...") + self._tracker.reset() + del self._tracker + self._tracker = None + if self._motion_estimator is not None: + self._motion_estimator.clear() + del self._motion_estimator + self._motion_estimator = None + + def _apply_slice(self, ply_path: Path, frame_stem: str) -> Path | None: + """对点云执行半径切片,只保留摄像机附近的点。 + + Args: + ply_path: 原始 PLY 文件路径 + frame_stem: 帧文件名(不含扩展名) + + Returns: + 切片后的临时 PLY 文件路径,失败返回 None + """ + import numpy as np + + try: + from plyfile import PlyData, PlyElement + except ImportError: + logger.warning("plyfile not installed, skipping slice") + return None + + logger.info(f"Applying slice (radius: {self.config.slice_radius}m)") + + try: + # 读取点云 + ply_data = PlyData.read(str(ply_path)) + vertex = ply_data["vertex"] + + # 获取坐标 + x = np.array(vertex["x"], dtype=np.float64) + z = np.array(vertex["z"], dtype=np.float64) + + # 计算水平距离(X-Z平面,以原点为圆心) + horizontal_dist = np.sqrt(x**2 + z**2) + + # 筛选在半径内的点 + mask = horizontal_dist <= self.config.slice_radius + kept_count = mask.sum() + total_count = len(mask) + + if kept_count == 0: + logger.warning("No points within slice radius, skipping slice") + return None + + logger.info( + f" Slice: {kept_count}/{total_count} points " + f"({100*kept_count/total_count:.1f}%)" + ) + + # 创建新的顶点数据 + new_vertex_data = vertex.data[mask] + new_vertex = PlyElement.describe(new_vertex_data, "vertex") + + # 保存到临时文件 + sliced_path = ply_path.parent / f"sliced_{frame_stem}.ply" + PlyData([new_vertex], text=False).write(str(sliced_path)) + + return sliced_path + + except Exception as e: + logger.warning(f"Failed to apply slice: {e}") + return None + def _apply_semantic_fusion( self, frame_path: Path, @@ -187,8 +298,10 @@ def _apply_semantic_fusion( detections_dir: Path, navigation_dir: Path | None = None, focal_length: float | None = None, + frame_id: int = 0, + timestamp: float = 0.0, ) -> None: - """对体素化后的点云应用语义融合。 + """对体素化后的点云应用语义融合和目标跟踪。 Args: frame_path: 原始帧图像路径 @@ -196,9 +309,10 @@ def _apply_semantic_fusion( detections_dir: 检测结果图片保存目录 navigation_dir: 导航用点云输出目录(机器人坐标系) focal_length: SHARP 推理时使用的焦距(像素),如果为 None 则估算 + frame_id: 帧 ID(用于跟踪) + timestamp: 时间戳(秒,用于运动估计) """ import cv2 - import numpy as np from aylm.tools.semantic_fusion import FusionConfig, SemanticFusion from aylm.tools.semantic_types import CameraIntrinsics @@ -217,8 +331,14 @@ def _apply_semantic_fusion( height, width = image.shape[:2] # 执行目标检测 + assert self._detector is not None, "Detector not initialized" detections = self._detector.detect(image, return_masks=True) - logger.debug(f"Detected {len(detections)} objects") + logger.info(f"Detected {len(detections)} objects in {frame_path.name}") + for det in detections: + logger.debug( + f" - {det.semantic_label.name} (class_id={det.class_id}, " + f"conf={det.confidence:.2f})" + ) # 保存检测结果可视化图片 if detections: @@ -228,6 +348,15 @@ def _apply_semantic_fusion( ) logger.debug(f"Detection image saved: {detection_image_path.name}") + # 目标跟踪(如果启用) + tracked_objects: list[TrackedObject] = [] + if self.config.enable_tracking and self._tracker is not None: + tracked_objects = self._tracker.update(detections, frame_id) + logger.info( + f"Tracking: {len(tracked_objects)} confirmed tracks " + f"(total: {self._tracker.track_count})" + ) + if not detections: logger.debug("No detections, skipping semantic fusion") return @@ -297,11 +426,22 @@ def _apply_semantic_fusion( intrinsics=intrinsics, ) + # 计算运动信息并导出带跟踪信息的 JSON if obstacles: json_path = ( voxel_ply_path.parent / f"{voxel_ply_path.stem}_obstacles.json" ) - marker.export_to_json(obstacles, json_path) + self._export_obstacles_with_tracking( + obstacles=obstacles, + tracked_objects=tracked_objects, + detections=detections, + points=points, + intrinsics=intrinsics, + image_shape=(height, width), + frame_id=frame_id, + timestamp=timestamp, + output_path=json_path, + ) logger.debug(f"Obstacles exported: {json_path.name}") # 输出导航用点云(机器人坐标系) @@ -314,6 +454,152 @@ def _apply_semantic_fusion( logger.warning(f"Semantic fusion failed: {e}") logger.exception(f"Semantic fusion error - {frame_path.name}") + def _export_obstacles_with_tracking( + self, + obstacles: list, + tracked_objects: list[TrackedObject], + detections: list, + points: np.ndarray, + intrinsics, + image_shape: tuple[int, int], + frame_id: int, + timestamp: float, + output_path: Path, + ) -> None: + """导出带跟踪和运动信息的障碍物 JSON。 + + Args: + obstacles: 障碍物列表 + tracked_objects: 跟踪对象列表 + detections: 检测结果列表 + points: 3D 点云 + intrinsics: 相机内参 + image_shape: 图像尺寸 (height, width) + frame_id: 帧 ID + timestamp: 时间戳 + output_path: 输出路径 + """ + from aylm.tools.coordinate_utils import opencv_to_robot + from aylm.tools.obstacle_marker import MOVABLE_LABELS + + # 建立检测到跟踪对象的映射(基于 IoU) + det_to_track: dict[int, TrackedObject] = {} + if tracked_objects: + for det_idx, det in enumerate(detections): + best_iou = 0.0 + best_track = None + for track in tracked_objects: + iou = self._compute_iou(det.bbox, track.bbox) + if iou > best_iou and iou > 0.3: + best_iou = iou + best_track = track + if best_track is not None: + det_to_track[det_idx] = best_track + + # 构建带运动信息的障碍物数据 + obstacles_data = [] + for obs_idx, obs in enumerate(obstacles): + obs_dict = obs.to_dict() + + # 查找对应的跟踪对象 + track = det_to_track.get(obs_idx) + if track is not None: + obs_dict["track_id"] = track.track_id + obs_dict["track_age"] = track.age + obs_dict["track_hits"] = track.hits + + # 计算 3D 中心点并更新运动估计 + center_cv = np.array(obs.center, dtype=np.float32) + if self._motion_estimator is not None: + motion = self._motion_estimator.update( + track_id=track.track_id, + position_cv=center_cv, + frame_id=frame_id, + timestamp=timestamp, + ) + if motion is not None: + obs_dict["motion"] = { + "velocity_cv": motion.velocity_cv.tolist(), + "velocity_robot": motion.velocity_robot.tolist(), + "speed": motion.speed, + "heading": motion.heading, + "is_stationary": motion.is_stationary, + } + logger.debug( + f"Track {track.track_id}: speed={motion.speed:.2f}m/s, " + f"stationary={motion.is_stationary}" + ) + + # 预测未来位置(1秒后) + predicted_pos = self._motion_estimator.predict( + track.track_id, dt=1.0 + ) + if predicted_pos is not None: + predicted_robot = opencv_to_robot(predicted_pos) + obs_dict["predicted_position_1s"] = { + "cv": predicted_pos.tolist(), + "robot": predicted_robot.tolist(), + } + else: + obs_dict["track_id"] = None + + obstacles_data.append(obs_dict) + + # 统计 + movable_count = sum(1 for obs in obstacles if obs.label in MOVABLE_LABELS) + static_count = len(obstacles) - movable_count + tracked_count = sum(1 for d in obstacles_data if d.get("track_id") is not None) + + data = { + "frame_id": frame_id, + "timestamp": timestamp, + "coordinate_systems": { + "cv": { + "description": "OpenCV/相机坐标系", + "axes": "X右, Y下, Z前", + }, + "robot": { + "description": "机器人/ROS坐标系", + "axes": "X前, Y左, Z上", + }, + "transform": "X_robot=Z_cv, Y_robot=-X_cv, Z_robot=-Y_cv", + }, + "total_count": len(obstacles), + "movable_count": movable_count, + "static_count": static_count, + "tracked_count": tracked_count, + "obstacles": obstacles_data, + } + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + + logger.info( + f"Exported {len(obstacles)} obstacles ({tracked_count} tracked) " + f"to {output_path.name}" + ) + + @staticmethod + def _compute_iou(bbox1: np.ndarray, bbox2: np.ndarray) -> float: + """计算两个边界框的 IoU。""" + x1 = max(bbox1[0], bbox2[0]) + y1 = max(bbox1[1], bbox2[1]) + x2 = min(bbox1[2], bbox2[2]) + y2 = min(bbox1[3], bbox2[3]) + + if x2 <= x1 or y2 <= y1: + return 0.0 + + intersection = (x2 - x1) * (y2 - y1) + area1 = (bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1]) + area2 = (bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1]) + union = area1 + area2 - intersection + + if union <= 0: + return 0.0 + + return float(intersection / union) + def _save_navigation_ply( self, semantic_pc, @@ -365,7 +651,7 @@ def _extraction_worker( ) # 逐帧提取并放入队列 - for frame_index, timestamp in frame_indices: + for frame_index, _timestamp in frame_indices: if self._stop_event.is_set(): break output_format = video_config.output_format.lower() @@ -389,8 +675,9 @@ def _process_frame( voxel_output_dir: Path, detections_dir: Path | None = None, navigation_dir: Path | None = None, + frame_id: int = 0, ) -> bool: - """处理单帧:推理 + 体素化 + 语义融合(可选)。 + """处理单帧:推理 + 体素化 + 语义融合 + 跟踪(可选)。 Args: frame_info: 帧信息 @@ -398,13 +685,14 @@ def _process_frame( voxel_output_dir: 体素化输出目录 detections_dir: 检测结果图片目录 navigation_dir: 导航用点云输出目录(机器人坐标系) + frame_id: 帧 ID(用于跟踪) Returns: 是否处理成功 """ - import torch.nn.functional as F from sharp.utils import io from sharp.utils.gaussians import save_ply, unproject_gaussians + from torch.nn import functional as functional_nn frame_path = frame_info.output_path if frame_path is None or not frame_path.exists(): @@ -415,15 +703,16 @@ def _process_frame( image, _, f_px = io.load_rgb(frame_path) height, width = image.shape[:2] - # 预处理 - internal_shape = (1536, 1536) + # 预处理(使用配置的内部分辨率) + res = self.config.internal_resolution + internal_shape = (res, res) image_pt = ( torch.from_numpy(image.copy()).float().to(self._device).permute(2, 0, 1) / 255.0 ) disparity_factor = torch.tensor([f_px / width]).float().to(self._device) - image_resized_pt = F.interpolate( + image_resized_pt = functional_nn.interpolate( image_pt[None], size=(internal_shape[1], internal_shape[0]), mode="bilinear", @@ -431,6 +720,7 @@ def _process_frame( ) # 推理 + assert self._predictor is not None, "Model not loaded" gaussians_ndc = self._predictor(image_resized_pt, disparity_factor) # 后处理 @@ -462,23 +752,44 @@ def _process_frame( ply_path = ply_output_dir / f"{frame_path.stem}.ply" save_ply(gaussians, f_px, (height, width), ply_path) + # 切片(如果启用) + input_ply_path = ply_path + if self.config.enable_slice: + sliced_path = self._apply_slice(ply_path, frame_path.stem) + if sliced_path is not None: + input_ply_path = sliced_path + # 体素化 voxel_path = voxel_output_dir / f"vox_{frame_path.stem}.ply" + assert self._voxelizer is not None, "Voxelizer not initialized" self._voxelizer.process( - ply_path, + input_ply_path, voxel_path, remove_ground=self.config.remove_ground, transform_coords=self.config.transform_coords, ) + # 清理临时切片文件 + if self.config.enable_slice and input_ply_path != ply_path: + with contextlib.suppress(Exception): + input_ply_path.unlink() + # 语义融合(如果启用) if self.config.enable_semantic and self._detector is not None: + # 计算时间戳 + timestamp = ( + frame_info.timestamp + if frame_info.timestamp + else (frame_id / self.config.motion_fps) + ) self._apply_semantic_fusion( frame_path, voxel_path, detections_dir or voxel_output_dir, navigation_dir=navigation_dir, focal_length=f_px, # 传递 SHARP 的精确焦距 + frame_id=frame_id, + timestamp=timestamp, ) return True @@ -555,6 +866,12 @@ def process( if self.config.enable_semantic: self._load_detector() + # 加载跟踪器(如果启用) + if self.config.enable_tracking: + self._load_tracker() + if self.config.verbose: + logger.info("Object tracking: ENABLED") + self._stop_event.clear() self._extraction_done.clear() self._frame_queue = queue.Queue(maxsize=self.config.frame_queue_size) @@ -582,6 +899,7 @@ def process( voxel_dir, detections_dir, navigation_dir, + frame_id=processed_count, ) process_time = time.time() - process_start @@ -591,9 +909,14 @@ def process( self.stats.total_processing_time += process_time if self.config.verbose: + output_name = ( + frame_info.output_path.name + if frame_info.output_path + else f"frame_{frame_info.index}" + ) logger.info( f"Processed frame {processed_count}: " - f"{frame_info.output_path.name} ({process_time:.2f}s)" + f"{output_name} ({process_time:.2f}s)" ) if progress_callback: @@ -613,6 +936,7 @@ def process( if self.config.auto_unload: self._unload_model() self._cleanup_detector() + self._cleanup_tracker() if self.config.verbose: logger.info("=" * 50) @@ -625,6 +949,8 @@ def process( logger.info(f" FPS: {self.stats.frames_per_second:.2f}") if self.config.enable_semantic: logger.info(" Semantic detection: ENABLED") + if self.config.enable_tracking: + logger.info(" Object tracking: ENABLED") if navigation_dir: logger.info(f" Navigation PLY: {navigation_dir}") logger.info("=" * 50) @@ -636,6 +962,7 @@ def cleanup(self): self._stop_event.set() self._unload_model() self._cleanup_detector() + self._cleanup_tracker() self._voxelizer = None @@ -651,6 +978,7 @@ def process_video( semantic_model: str = "yolo11n-seg.pt", semantic_confidence: float = 0.5, output_navigation_ply: bool = True, + enable_tracking: bool = True, ) -> VideoProcessingStats: """便捷函数:处理视频。 @@ -666,6 +994,7 @@ def process_video( semantic_model: YOLO 模型名称 semantic_confidence: 检测置信度阈值 output_navigation_ply: 是否输出导航用点云(机器人坐标系) + enable_tracking: 是否启用目标跟踪 Returns: VideoProcessingStats: 处理统计 @@ -676,6 +1005,7 @@ def process_video( ... video_path="video.mp4", ... output_dir="output/", ... enable_semantic=True, + ... enable_tracking=True, ... output_navigation_ply=True, ... ) >>> print(f"Processed {stats.total_frames_processed} frames") @@ -689,6 +1019,7 @@ def process_video( semantic_model=semantic_model, semantic_confidence=semantic_confidence, output_navigation_ply=output_navigation_ply, + enable_tracking=enable_tracking, ) processor = VideoPipelineProcessor(config) diff --git a/src/aylm/tools/voxel_player.py b/src/aylm/tools/voxel_player.py index aa1357f..a32a3a4 100644 --- a/src/aylm/tools/voxel_player.py +++ b/src/aylm/tools/voxel_player.py @@ -15,13 +15,10 @@ from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable +from typing import Any, Callable import numpy as np -if TYPE_CHECKING: - import open3d as o3d - logger = logging.getLogger(__name__) # 尝试导入Open3D @@ -37,7 +34,7 @@ # 尝试导入matplotlib作为fallback try: import matplotlib.pyplot as plt - from mpl_toolkits.mplot3d import Axes3D # noqa: F401 + from mpl_toolkits.mplot3d import Axes3D # noqa: F401 - required for 3d projection HAS_MATPLOTLIB = True except ImportError: @@ -304,7 +301,7 @@ def _playback_loop(self): time.sleep(sleep_time) # 键盘回调 - def _on_space(self, vis): + def _on_space(self, _vis): """空格键:播放/暂停。""" if self._state == PlaybackState.PLAYING: self._state = PlaybackState.PAUSED @@ -316,50 +313,50 @@ def _on_space(self, vis): logger.info("Playing") return False - def _on_right(self, vis): + def _on_right(self, _vis): """右箭头:下一帧。""" if self._current_index < len(self._ply_files) - 1: self._update_frame(self._current_index + 1) return False - def _on_left(self, vis): + def _on_left(self, _vis): """左箭头:上一帧。""" if self._current_index > 0: self._update_frame(self._current_index - 1) return False - def _on_up(self, vis): + def _on_up(self, _vis): """上箭头:加速。""" self._fps = min(60.0, self._fps * 1.25) logger.info(f"FPS: {self._fps:.1f}") return False - def _on_down(self, vis): + def _on_down(self, _vis): """下箭头:减速。""" self._fps = max(1.0, self._fps / 1.25) logger.info(f"FPS: {self._fps:.1f}") return False - def _on_reset(self, vis): + def _on_reset(self, _vis): """R键:重置到开头。""" self._update_frame(0) self._start_time = None logger.info("Reset to beginning") return False - def _on_loop_toggle(self, vis): + def _on_loop_toggle(self, _vis): """L键:切换循环模式。""" self._loop = not self._loop logger.info(f"Loop: {'ON' if self._loop else 'OFF'}") return False - def _on_center(self, vis): + def _on_center(self, _vis): """C键:重置视角。""" if self._vis is not None: self._vis.reset_view_point(True) return False - def _on_quit(self, vis): + def _on_quit(self, _vis): """Q键/Esc:退出。""" self._stop_event.set() self._state = PlaybackState.STOPPED @@ -497,11 +494,11 @@ def export_video( logger.info(f"Exporting video to {output_path}") logger.info(f" Resolution: {width}x{height}, FPS: {fps}") - fourcc = cv2.VideoWriter_fourcc(*"mp4v") + fourcc = cv2.VideoWriter_fourcc(*"mp4v") # type: ignore[attr-defined] writer = cv2.VideoWriter(str(output_path), fourcc, fps, (width, height)) try: - for i, ply_path in enumerate(self._ply_files): + for i, _ply_path in enumerate(self._ply_files): # 使用matplotlib渲染帧 pcd = self._load_frame(i) if pcd is None: @@ -537,8 +534,10 @@ def export_video( ax.set_title(f"Frame {i + 1}/{len(self._ply_files)}") fig.canvas.draw() - img = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8) - img = img.reshape(fig.canvas.get_width_height()[::-1] + (3,)) + img = np.frombuffer( + fig.canvas.tostring_rgb(), dtype=np.uint8 # type: ignore[attr-defined] + ) + img = img.reshape((*fig.canvas.get_width_height()[::-1], 3)) img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) writer.write(img_bgr) @@ -710,6 +709,7 @@ def play_sequence( >>> play_sequence("output/voxelized", fps=15, loop=True) """ config = PlayerConfig(fps=fps, loop=loop) + player: VoxelPlayer | MatplotlibVoxelPlayer if HAS_OPEN3D: player = VoxelPlayer(config) diff --git a/tests/unit/test_motion_estimator.py b/tests/unit/test_motion_estimator.py new file mode 100644 index 0000000..8fb1810 --- /dev/null +++ b/tests/unit/test_motion_estimator.py @@ -0,0 +1,378 @@ +"""运动矢量估计器模块测试。""" + +import numpy as np +import pytest + +from aylm.tools.motion_estimator import ( + KalmanConfig, + MotionEstimator, + MotionVector, + TrackedObject3D, + create_tracked_object, +) +from aylm.tools.semantic_types import SemanticLabel + + +class TestMotionVector: + """MotionVector 数据类测试。""" + + def test_creation(self): + """测试创建 MotionVector。""" + velocity_cv = np.array([1.0, 0.0, 2.0], dtype=np.float32) + velocity_robot = np.array([2.0, -1.0, 0.0], dtype=np.float32) + + mv = MotionVector( + velocity_cv=velocity_cv, + velocity_robot=velocity_robot, + speed=2.236, + heading=0.5, + is_stationary=False, + ) + + np.testing.assert_array_equal(mv.velocity_cv, velocity_cv) + np.testing.assert_array_equal(mv.velocity_robot, velocity_robot) + assert mv.speed == pytest.approx(2.236) + assert mv.heading == pytest.approx(0.5) + assert mv.is_stationary is False + + def test_stationary_flag(self): + """测试静止标志。""" + mv = MotionVector( + velocity_cv=np.array([0.01, 0.01, 0.01], dtype=np.float32), + velocity_robot=np.array([0.01, -0.01, -0.01], dtype=np.float32), + speed=0.017, + heading=0.0, + is_stationary=True, + ) + assert mv.is_stationary is True + + +class TestKalmanConfig: + """KalmanConfig 数据类测试。""" + + def test_default_values(self): + """测试默认配置值。""" + config = KalmanConfig() + assert config.process_noise == 0.1 + assert config.measurement_noise == 0.5 + assert config.initial_covariance == 1.0 + + def test_custom_values(self): + """测试自定义配置值。""" + config = KalmanConfig( + process_noise=0.2, + measurement_noise=0.3, + initial_covariance=2.0, + ) + assert config.process_noise == 0.2 + assert config.measurement_noise == 0.3 + assert config.initial_covariance == 2.0 + + +class TestMotionEstimator: + """MotionEstimator 类测试。""" + + def test_init_default(self): + """测试默认初始化。""" + estimator = MotionEstimator() + assert estimator.fps == 30.0 + assert estimator.stationary_threshold == 0.1 + assert len(estimator.active_tracks) == 0 + + def test_init_custom(self): + """测试自定义初始化。""" + config = KalmanConfig(process_noise=0.2) + estimator = MotionEstimator( + fps=60.0, + stationary_threshold=0.2, + kalman_config=config, + ) + assert estimator.fps == 60.0 + assert estimator.stationary_threshold == 0.2 + assert estimator.config.process_noise == 0.2 + + def test_first_update_returns_none(self): + """测试首次更新返回 None(无速度信息)。""" + estimator = MotionEstimator() + position = np.array([1.0, 2.0, 3.0]) + + result = estimator.update(track_id=1, position_cv=position, frame_id=0) + + assert result is None + assert 1 in estimator.active_tracks + + def test_second_update_returns_motion(self): + """测试第二次更新返回运动矢量。""" + estimator = MotionEstimator(fps=30.0) + + # 第一帧 + pos1 = np.array([0.0, 0.0, 0.0]) + estimator.update(track_id=1, position_cv=pos1, frame_id=0) + + # 第二帧,移动了 1 米(在 Z 方向) + pos2 = np.array([0.0, 0.0, 1.0]) + result = estimator.update(track_id=1, position_cv=pos2, frame_id=1) + + assert result is not None + assert isinstance(result, MotionVector) + # 速度应该大于 0 + assert result.speed > 0 + + def test_velocity_calculation(self): + """测试速度计算。""" + estimator = MotionEstimator(fps=1.0) # 1 FPS 便于计算 + + # 第一帧 + pos1 = np.array([0.0, 0.0, 0.0]) + estimator.update(track_id=1, position_cv=pos1, frame_id=0, timestamp=0.0) + + # 第二帧,1 秒后移动 1 米 + pos2 = np.array([1.0, 0.0, 0.0]) + result = estimator.update( + track_id=1, position_cv=pos2, frame_id=1, timestamp=1.0 + ) + + assert result is not None + # Kalman 滤波会平滑速度,但应该接近 1 m/s + assert result.speed > 0.5 + + def test_stationary_detection(self): + """测试静止检测。""" + estimator = MotionEstimator(fps=30.0, stationary_threshold=0.1) + + # 第一帧 + pos1 = np.array([0.0, 0.0, 5.0]) + estimator.update(track_id=1, position_cv=pos1, frame_id=0) + + # 第二帧,几乎不动 + pos2 = np.array([0.001, 0.001, 5.001]) + result = estimator.update(track_id=1, position_cv=pos2, frame_id=1) + + assert result is not None + # 速度很小,应该被判定为静止 + assert result.is_stationary is True + + def test_moving_detection(self): + """测试运动检测。""" + estimator = MotionEstimator(fps=1.0, stationary_threshold=0.1) + + # 第一帧 + pos1 = np.array([0.0, 0.0, 0.0]) + estimator.update(track_id=1, position_cv=pos1, frame_id=0, timestamp=0.0) + + # 第二帧,明显移动 + pos2 = np.array([2.0, 0.0, 0.0]) + result = estimator.update( + track_id=1, position_cv=pos2, frame_id=1, timestamp=1.0 + ) + + assert result is not None + assert result.is_stationary is False + + def test_predict_future_position(self): + """测试位置预测。""" + estimator = MotionEstimator(fps=1.0) + + # 建立轨迹 + estimator.update( + track_id=1, position_cv=np.array([0.0, 0.0, 0.0]), frame_id=0, timestamp=0.0 + ) + estimator.update( + track_id=1, position_cv=np.array([1.0, 0.0, 0.0]), frame_id=1, timestamp=1.0 + ) + + # 预测 1 秒后的位置 + predicted = estimator.predict(track_id=1, dt=1.0) + + assert predicted is not None + # 应该在 X 方向继续移动 + assert predicted[0] > 1.0 + + def test_predict_nonexistent_track(self): + """测试预测不存在的轨迹。""" + estimator = MotionEstimator() + result = estimator.predict(track_id=999, dt=1.0) + assert result is None + + def test_get_velocity(self): + """测试获取速度。""" + estimator = MotionEstimator(fps=1.0) + + # 建立轨迹 + estimator.update( + track_id=1, position_cv=np.array([0.0, 0.0, 0.0]), frame_id=0, timestamp=0.0 + ) + estimator.update( + track_id=1, position_cv=np.array([1.0, 0.0, 0.0]), frame_id=1, timestamp=1.0 + ) + + velocity = estimator.get_velocity(track_id=1) + + assert velocity is not None + assert len(velocity) == 3 + + def test_get_velocity_nonexistent_track(self): + """测试获取不存在轨迹的速度。""" + estimator = MotionEstimator() + result = estimator.get_velocity(track_id=999) + assert result is None + + def test_multiple_tracks(self): + """测试多目标同时跟踪。""" + estimator = MotionEstimator() + + # 跟踪两个目标 + estimator.update(track_id=1, position_cv=np.array([0.0, 0.0, 0.0]), frame_id=0) + estimator.update( + track_id=2, position_cv=np.array([10.0, 10.0, 10.0]), frame_id=0 + ) + + assert len(estimator.active_tracks) == 2 + assert 1 in estimator.active_tracks + assert 2 in estimator.active_tracks + + def test_remove_track(self): + """测试移除轨迹。""" + estimator = MotionEstimator() + + estimator.update(track_id=1, position_cv=np.array([0.0, 0.0, 0.0]), frame_id=0) + assert 1 in estimator.active_tracks + + result = estimator.remove_track(track_id=1) + assert result is True + assert 1 not in estimator.active_tracks + + def test_remove_nonexistent_track(self): + """测试移除不存在的轨迹。""" + estimator = MotionEstimator() + result = estimator.remove_track(track_id=999) + assert result is False + + def test_clear(self): + """测试清除所有轨迹。""" + estimator = MotionEstimator() + + # 添加多个轨迹 + for i in range(5): + estimator.update( + track_id=i, position_cv=np.array([float(i), 0.0, 0.0]), frame_id=0 + ) + + assert len(estimator.active_tracks) == 5 + + estimator.clear() + assert len(estimator.active_tracks) == 0 + + def test_heading_calculation(self): + """测试航向角计算。""" + estimator = MotionEstimator(fps=1.0) + + # 第一帧 + estimator.update( + track_id=1, position_cv=np.array([0.0, 0.0, 0.0]), frame_id=0, timestamp=0.0 + ) + + # 第二帧,沿 Z 轴正方向移动(OpenCV 坐标系) + # 在机器人坐标系中,这对应 X 轴正方向(前进) + result = estimator.update( + track_id=1, position_cv=np.array([0.0, 0.0, 5.0]), frame_id=1, timestamp=1.0 + ) + + assert result is not None + # 航向角应该接近 0(正前方) + assert abs(result.heading) < 1.0 # 允许一定误差 + + def test_kalman_smoothing(self): + """测试 Kalman 滤波平滑效果。""" + estimator = MotionEstimator(fps=10.0) + + # 模拟带噪声的轨迹 + np.random.seed(42) + base_positions = [np.array([float(i), 0.0, 0.0]) for i in range(10)] + noisy_positions = [p + np.random.normal(0, 0.1, 3) for p in base_positions] + + results = [] + for i, pos in enumerate(noisy_positions): + result = estimator.update(track_id=1, position_cv=pos, frame_id=i) + if result is not None: + results.append(result) + + # 速度应该相对稳定(Kalman 滤波平滑了噪声) + speeds = [r.speed for r in results] + speed_std = np.std(speeds) + + # 标准差应该较小(平滑效果) + assert speed_std < 5.0 # 合理的阈值 + + +class TestTrackedObject3D: + """TrackedObject3D 数据类测试。""" + + def test_creation(self): + """测试创建 TrackedObject3D。""" + obj = TrackedObject3D( + track_id=1, + center_cv=np.array([1.0, 2.0, 3.0], dtype=np.float32), + center_robot=np.array([3.0, -1.0, -2.0], dtype=np.float32), + dimensions=np.array([0.5, 1.7, 0.4], dtype=np.float32), + motion=None, + semantic_label=SemanticLabel.PERSON, + confidence=0.95, + frame_id=10, + timestamp=0.333, + ) + + assert obj.track_id == 1 + assert obj.semantic_label == SemanticLabel.PERSON + assert obj.confidence == 0.95 + assert obj.frame_id == 10 + assert obj.timestamp == pytest.approx(0.333) + assert obj.motion is None + + +class TestCreateTrackedObject: + """create_tracked_object 辅助函数测试。""" + + def test_basic_creation(self): + """测试基本创建。""" + obj = create_tracked_object( + track_id=1, + center_cv=np.array([0.0, 0.0, 5.0]), + dimensions=np.array([0.6, 1.7, 0.4]), + semantic_label=SemanticLabel.PERSON, + confidence=0.9, + frame_id=0, + timestamp=0.0, + ) + + assert obj.track_id == 1 + assert obj.semantic_label == SemanticLabel.PERSON + assert obj.confidence == 0.9 + # 验证坐标系转换 + assert obj.center_cv is not None + assert obj.center_robot is not None + + def test_with_motion(self): + """测试带运动信息的创建。""" + motion = MotionVector( + velocity_cv=np.array([0.0, 0.0, 1.0], dtype=np.float32), + velocity_robot=np.array([1.0, 0.0, 0.0], dtype=np.float32), + speed=1.0, + heading=0.0, + is_stationary=False, + ) + + obj = create_tracked_object( + track_id=1, + center_cv=np.array([0.0, 0.0, 5.0]), + dimensions=np.array([0.6, 1.7, 0.4]), + semantic_label=SemanticLabel.VEHICLE, + confidence=0.85, + frame_id=10, + timestamp=0.333, + motion=motion, + ) + + assert obj.motion is not None + assert obj.motion.speed == 1.0 + assert obj.motion.is_stationary is False diff --git a/tests/unit/test_object_tracker.py b/tests/unit/test_object_tracker.py new file mode 100644 index 0000000..5aca646 --- /dev/null +++ b/tests/unit/test_object_tracker.py @@ -0,0 +1,311 @@ +"""多目标跟踪器模块测试。""" + +import numpy as np +import pytest + +from aylm.tools.object_tracker import ( + MultiObjectTracker, + TrackedObject, + TrackerConfig, + _compute_iou, +) +from aylm.tools.semantic_types import Detection2D, SemanticLabel + + +class TestComputeIoU: + """IoU 计算测试。""" + + def test_identical_boxes(self): + """测试完全重叠的边界框。""" + bbox = np.array([0, 0, 100, 100], dtype=np.float32) + iou = _compute_iou(bbox, bbox) + assert iou == pytest.approx(1.0) + + def test_no_overlap(self): + """测试无重叠的边界框。""" + bbox1 = np.array([0, 0, 50, 50], dtype=np.float32) + bbox2 = np.array([100, 100, 150, 150], dtype=np.float32) + iou = _compute_iou(bbox1, bbox2) + assert iou == 0.0 + + def test_partial_overlap(self): + """测试部分重叠的边界框。""" + bbox1 = np.array([0, 0, 100, 100], dtype=np.float32) + bbox2 = np.array([50, 50, 150, 150], dtype=np.float32) + # 交集: 50x50 = 2500 + # 并集: 10000 + 10000 - 2500 = 17500 + # IoU = 2500 / 17500 ≈ 0.143 + iou = _compute_iou(bbox1, bbox2) + assert iou == pytest.approx(2500 / 17500, rel=0.01) + + def test_one_inside_other(self): + """测试一个边界框完全在另一个内部。""" + bbox1 = np.array([0, 0, 100, 100], dtype=np.float32) + bbox2 = np.array([25, 25, 75, 75], dtype=np.float32) + # 交集: 50x50 = 2500 + # 并集: 10000 (大框面积) + # IoU = 2500 / 10000 = 0.25 + iou = _compute_iou(bbox1, bbox2) + assert iou == pytest.approx(0.25, rel=0.01) + + +class TestTrackedObject: + """TrackedObject 数据类测试。""" + + def test_creation(self): + """测试创建 TrackedObject。""" + bbox = np.array([10, 20, 110, 120], dtype=np.float32) + obj = TrackedObject( + track_id=1, + bbox=bbox, + class_id=0, + confidence=0.9, + age=5, + hits=3, + time_since_update=0, + ) + assert obj.track_id == 1 + assert obj.class_id == 0 + assert obj.confidence == 0.9 + assert obj.age == 5 + assert obj.hits == 3 + np.testing.assert_array_equal(obj.bbox, bbox) + + def test_bbox_type_conversion(self): + """测试 bbox 类型自动转换。""" + # 使用列表创建 + obj = TrackedObject( + track_id=1, + bbox=[10, 20, 110, 120], # type: ignore + class_id=0, + confidence=0.9, + ) + assert obj.bbox.dtype == np.float32 + assert isinstance(obj.bbox, np.ndarray) + + +class TestMultiObjectTracker: + """MultiObjectTracker 类测试。""" + + def _create_detection( + self, + bbox: list[float], + class_id: int = 0, + confidence: float = 0.9, + ) -> Detection2D: + """创建测试用检测结果。""" + return Detection2D( + bbox=np.array(bbox, dtype=np.float32), + mask=None, + class_id=class_id, + confidence=confidence, + semantic_label=SemanticLabel.PERSON, + ) + + def test_init_default_config(self): + """测试默认配置初始化。""" + tracker = MultiObjectTracker() + assert tracker.config.max_age == 30 + assert tracker.config.min_hits == 3 + assert tracker.config.iou_threshold == 0.3 + + def test_init_custom_config(self): + """测试自定义配置初始化。""" + config = TrackerConfig(max_age=10, min_hits=2, iou_threshold=0.5) + tracker = MultiObjectTracker(config=config) + assert tracker.config.max_age == 10 + assert tracker.config.min_hits == 2 + assert tracker.config.iou_threshold == 0.5 + + def test_init_with_params(self): + """测试使用参数初始化。""" + tracker = MultiObjectTracker(max_age=20, min_hits=5, iou_threshold=0.4) + assert tracker.config.max_age == 20 + assert tracker.config.min_hits == 5 + assert tracker.config.iou_threshold == 0.4 + + def test_first_frame_creates_tracks(self): + """测试第一帧创建轨迹。""" + tracker = MultiObjectTracker(min_hits=1) + detections = [ + self._create_detection([0, 0, 50, 50]), + self._create_detection([100, 100, 150, 150]), + ] + + tracked = tracker.update(detections, frame_id=0) + + # min_hits=1,第一帧就应该返回确认轨迹 + assert tracker.track_count == 2 + assert len(tracked) == 2 + + def test_track_id_assignment(self): + """测试 track_id 分配。""" + tracker = MultiObjectTracker(min_hits=1) + detections = [ + self._create_detection([0, 0, 50, 50]), + self._create_detection([100, 100, 150, 150]), + ] + + tracked = tracker.update(detections, frame_id=0) + + # 验证 track_id 是唯一的 + track_ids = [t.track_id for t in tracked] + assert len(track_ids) == len(set(track_ids)) + assert 1 in track_ids + assert 2 in track_ids + + def test_track_association(self): + """测试跨帧轨迹关联。""" + tracker = MultiObjectTracker(min_hits=1, iou_threshold=0.3) + + # 第一帧 + det1 = [self._create_detection([0, 0, 50, 50])] + tracked1 = tracker.update(det1, frame_id=0) + track_id = tracked1[0].track_id + + # 第二帧,目标稍微移动 + det2 = [self._create_detection([5, 5, 55, 55])] + tracked2 = tracker.update(det2, frame_id=1) + + # 应该保持相同的 track_id + assert len(tracked2) == 1 + assert tracked2[0].track_id == track_id + + def test_new_track_creation(self): + """测试新目标出现时创建新轨迹。""" + tracker = MultiObjectTracker(min_hits=1) + + # 第一帧:一个目标 + det1 = [self._create_detection([0, 0, 50, 50])] + tracker.update(det1, frame_id=0) + + # 第二帧:两个目标(原有 + 新增) + det2 = [ + self._create_detection([0, 0, 50, 50]), + self._create_detection([200, 200, 250, 250]), + ] + tracked = tracker.update(det2, frame_id=1) + + assert tracker.track_count == 2 + assert len(tracked) == 2 + + def test_track_deletion_after_max_age(self): + """测试超过 max_age 后删除轨迹。""" + tracker = MultiObjectTracker(max_age=3, min_hits=1) + + # 第一帧:创建轨迹 + det1 = [self._create_detection([0, 0, 50, 50])] + tracker.update(det1, frame_id=0) + assert tracker.track_count == 1 + + # 后续帧:无检测 + for i in range(1, 5): + tracker.update([], frame_id=i) + + # 轨迹应该被删除 + assert tracker.track_count == 0 + + def test_min_hits_confirmation(self): + """测试 min_hits 确认机制。""" + tracker = MultiObjectTracker(min_hits=3) + + det = [self._create_detection([0, 0, 50, 50])] + + # 第一帧:未确认 + tracked1 = tracker.update(det, frame_id=0) + assert len(tracked1) == 0 + + # 第二帧:未确认 + tracked2 = tracker.update(det, frame_id=1) + assert len(tracked2) == 0 + + # 第三帧:确认 + tracked3 = tracker.update(det, frame_id=2) + assert len(tracked3) == 1 + + def test_reset(self): + """测试重置跟踪器。""" + tracker = MultiObjectTracker(min_hits=1) + + det = [self._create_detection([0, 0, 50, 50])] + tracker.update(det, frame_id=0) + assert tracker.track_count == 1 + + tracker.reset() + assert tracker.track_count == 0 + assert tracker.confirmed_track_count == 0 + + def test_get_all_tracks(self): + """测试获取所有轨迹(包括未确认的)。""" + tracker = MultiObjectTracker(min_hits=3) + + det = [self._create_detection([0, 0, 50, 50])] + tracker.update(det, frame_id=0) + + # 未确认,但应该在 all_tracks 中 + all_tracks = tracker.get_all_tracks() + assert len(all_tracks) == 1 + + def test_empty_detections(self): + """测试空检测列表。""" + tracker = MultiObjectTracker(min_hits=1) + + # 空检测不应该崩溃 + tracked = tracker.update([], frame_id=0) + assert len(tracked) == 0 + assert tracker.track_count == 0 + + def test_track_hits_increment(self): + """测试 hits 计数递增。""" + tracker = MultiObjectTracker(min_hits=1) + + det = [self._create_detection([0, 0, 50, 50])] + + # 连续更新 + for i in range(5): + tracked = tracker.update(det, frame_id=i) + + # hits 应该是 5 + assert len(tracked) == 1 + assert tracked[0].hits == 5 + + def test_time_since_update(self): + """测试 time_since_update 字段。""" + tracker = MultiObjectTracker(min_hits=1, max_age=10) + + det = [self._create_detection([0, 0, 50, 50])] + + # 第一帧 + tracker.update(det, frame_id=0) + + # 后续帧无检测 + tracker.update([], frame_id=1) + tracker.update([], frame_id=2) + + # 获取所有轨迹(包括未匹配的) + all_tracks = tracker.get_all_tracks() + assert len(all_tracks) == 1 + assert all_tracks[0].time_since_update == 2 + + def test_class_id_preserved(self): + """测试 class_id 保持不变。""" + tracker = MultiObjectTracker(min_hits=1) + + det = [self._create_detection([0, 0, 50, 50], class_id=2)] + tracked = tracker.update(det, frame_id=0) + + assert tracked[0].class_id == 2 + + def test_confidence_updated(self): + """测试置信度更新。""" + tracker = MultiObjectTracker(min_hits=1) + + # 第一帧 + det1 = [self._create_detection([0, 0, 50, 50], confidence=0.8)] + tracker.update(det1, frame_id=0) + + # 第二帧,置信度变化 + det2 = [self._create_detection([0, 0, 50, 50], confidence=0.95)] + tracked = tracker.update(det2, frame_id=1) + + assert tracked[0].confidence == 0.95