{"name":"napari-mlarray","display_name":"MLArray","visibility":"public","icon":null,"categories":["IO"],"schema_version":"0.3.0","on_activate":null,"on_deactivate":null,"contributions":{"commands":[{"id":"napari-mlarray.get_reader","title":"Open MLArray (.mla) files","python_name":"napari_mlarray._reader:napari_get_reader","short_title":null,"category":null,"icon":null,"enablement":null},{"id":"napari-mlarray.write_single_image","title":"Save as MLArray (.mla)","python_name":"napari_mlarray._writer:write_single_image","short_title":null,"category":null,"icon":null,"enablement":null}],"readers":[{"command":"napari-mlarray.get_reader","filename_patterns":["*.mla"],"accepts_directories":false}],"writers":[{"command":"napari-mlarray.write_single_image","layer_types":["image","labels"],"filename_extensions":[".mla"],"display_name":""}],"widgets":null,"sample_data":null,"themes":null,"menus":{},"submenus":null,"keybindings":null,"configurations":{}},"package_metadata":{"metadata_version":"2.4","name":"napari-mlarray","version":"1.1.0","dynamic":["license-file"],"platform":null,"supported_platform":null,"summary":"napari reader and writer for MLArray (.mla) images, with correct physical geometry and orientation.","description":"# napari-mlarray\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/napari-mlarray/\"><img src=\"https://img.shields.io/pypi/v/napari-mlarray?logo=pypi&color=brightgreen\" alt=\"PyPI\" align=\"middle\" /></a>\n  <a href=\"https://pypi.org/project/napari-mlarray/\"><img src=\"https://img.shields.io/pypi/pyversions/napari-mlarray?logo=python\" alt=\"Python Version\" align=\"middle\" /></a>\n  <a href=\"https://github.com/MIC-DKFZ/napari-mlarray/actions\"><img src=\"https://img.shields.io/github/actions/workflow/status/MIC-DKFZ/napari-mlarray/test_and_deploy.yml?branch=main&logo=github\" alt=\"Tests\" align=\"middle\" /></a>\n  <a href=\"https://napari-hub.org/plugins/napari-mlarray\"><img src=\"https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-mlarray\" alt=\"napari hub\" align=\"middle\" /></a>\n  <a href=\"https://github.com/MIC-DKFZ/napari-mlarray/blob/main/LICENSE\"><img src=\"https://img.shields.io/github/license/MIC-DKFZ/napari-mlarray\" alt=\"License\" align=\"middle\" /></a>\n</p>\n\nA [napari](https://napari.org) reader and writer for [MLArray](https://github.com/MIC-DKFZ/mlarray) (`.mla`) files\nthat shows every image where it physically is, in the orientation radiologists expect.\n\n```bash\npip install napari-mlarray            # into an existing napari installation\npip install \"napari-mlarray[all]\"     # with napari and Qt\n```\n\nDrag a `.mla` file into napari, or `File > Open`. To convert NIfTI, NRRD, MetaImage, MGH or DICOM to `.mla`, use\n[MedVol](https://mic-dkfz.github.io/MedVol/): `medvol convert ct.nii.gz ct.mla`.\n\n## What you see\n\nnapari knows nothing about patient anatomy: a layer is an array plus an affine into \"world\" coordinates, and the\n2-D canvas shows the last two world dims (down and right). napari-mlarray chooses the world so that the default\ncamera gives the familiar picture, with no flips or rotations to fix by hand:\n\n| plane (`viewer.dims.order`) | screen up | screen right |\n|---|---|---|\n| axial `(0, 1, 2)` | anterior | patient left (radiological) |\n| coronal `(1, 0, 2)` | superior | patient left (radiological) |\n| sagittal `(2, 0, 1)` | superior | posterior |\n\n- The world axes of a patient image point to **I, P, L** (labelled in napari's dims slider). This frame is\n  right-handed, so the 3-D view is not mirrored. With the `neurological` convention they point to **I, P, R**\n  (patient left on the screen's left) and the plugin flips the 3-D camera depth to keep 3-D unmirrored.\n- Every voxel is placed exactly from the file's geometry (`spaces[0]`): any storage order (axial, sagittal,\n  coronal, flipped) gives the same picture, and images of different grids overlay correctly. The array is\n  reordered for display only, never resampled, unless the grid is oblique (below).\n- World coordinates are millimetres. Convert the cursor position back to the file's patient coordinates (DICOM\n  LPS) with `napari_mlarray.letters_to_lps(viewer.dims.axis_labels[-3:]).T @ viewer.cursor.position[-3:]`.\n\nThis is verified on screen by [`scripts/verify_orientation.py`](scripts/verify_orientation.py) (napari 0.10): a\nletter phantom stored axially, RAS-flipped, sagittally and obliquely gives the same, correct picture in all three\nplanes and an unmirrored 3-D view, in both conventions. Other spaces work the same way:\nearth-observation spaces are shown north up and east right, spaces without direction words in reversed axis order\n(`z, y, x`), so an identity matrix shows the array as NumPy does.\n\n### Oblique images\n\nnapari cannot slice an affine that rotates the displayed axes against the slicing axis in 2-D (it drops the\nout-of-plane rotation). For grids that are oblique to the world axes, choose:\n\n| `oblique` | effect |\n|---|---|\n| `resample` (default) | resampled onto a world-aligned grid (linear for images, nearest neighbour for labels): exact positions |\n| `nearest` | axes snapped to the closest world axes, as ITK-SNAP and MRIcroGL do: upright, but positions are approximate |\n| `keep` | the exact affine: correct in 3-D, approximate in 2-D slices |\n\n### Settings\n\n```python\nimport napari_mlarray\n\nnapari_mlarray.set_display(convention=\"neurological\", oblique=\"nearest\")   # for files opened from now on\nlayers = napari_mlarray.add_mlarray(viewer, \"ct.mla\", convention=\"radiological\")   # one file, explicit options\n```\n\nor set `NAPARI_MLARRAY_CONVENTION` / `NAPARI_MLARRAY_OBLIQUE` before starting napari.\n\n## Layers\n\n| content of the file | napari layer |\n|---|---|\n| `values.kind` `measurement` (and others) | Image; `values.scale`/`offset` applied (e.g. Hounsfield units), contrast limits from `ml` statistics when present |\n| `values.kind` `label` or `instance` | Labels, with names, colours and codes of `values.labels` (shown when hovering) |\n| a `channel` axis | one Image layer per channel, named after the axis labels (`ct [T1]`, `ct [T2]`, ...) |\n| `time` and other axes | sliders; time is in its unit (`start`, `step`) |\n| a 2-D grid in a 3-D space (a single DICOM slice) | a one-slice volume at its position, so it overlays with volumes |\n| `profiles.ml.boxes` (3-D) | Labels layer of box outlines: every slice in every plane shows the box, and so does 3-D; hovering shows label and score |\n| `profiles.ml.boxes` (2-D) | Shapes rectangles with label and score |\n\nBoxes are drawn by [napari-bboxes](https://github.com/Karol-G/napari-bboxes), which you can also use on its own for\nboxes from any source.\n\nFiles larger than 1 GiB are read slice by slice and stay on disk. Labels are always loaded, so you can paint.\n\n## Saving\n\n`File > Save Selected Layer(s)` with a `.mla` name writes Image and Labels layers:\n\n- A layer opened from a `.mla` file is written in its original array order with all its metadata: axes, values and\n  label table, profiles, user metadata, source headers and history (plus an `edited` entry). Value scaling applied\n  for display is undone when that is exact. The geometry comes from the layer's current transform, so a layer\n  you moved is saved where it is shown.\n- A new layer (for example labels you painted on an image) is written in the patient frame (LPS) when the viewer\n  shows patient axes (`I, P, L` or `I, P, R`), so it lines up with the image in any other tool. Label names from\n  the layer's features (`index`, `name`) become the label table.\n\n## Development\n\n```bash\npip install -e . --group dev\npytest\n```\n\nThe tests check every voxel's world position for axial, sagittal, flipped and RAS-stored grids in both\nconventions, oblique grids, labels, channels, time, single slices, boxes and save/load round trips.\n`xvfb-run python scripts/verify_orientation.py OUTDIR` checks the picture on screen.\n\n## License\n\nDistributed under the terms of the MIT license.\n\n## Acknowledgments\n\n<p align=\"left\">\n  <img src=\"https://github.com/MIC-DKFZ/vidata/raw/main/imgs/Logos/HI_Logo.png\" width=\"150\"> &nbsp;&nbsp;&nbsp;&nbsp;\n  <img src=\"https://github.com/MIC-DKFZ/vidata/raw/main/imgs/Logos/DKFZ_Logo.png\" width=\"500\">\n</p>\n\nThis repository is developed and maintained by the Applied Computer Vision Lab (ACVL)\nof [Helmholtz Imaging](https://www.helmholtz-imaging.de/) and the\n[Division of Medical Image Computing](https://www.dkfz.de/en/medical-image-computing) at DKFZ.\n","description_content_type":"text/markdown","keywords":null,"home_page":null,"download_url":null,"author":"Karol-G","author_email":"karol.gotkowski@dkfz.de","maintainer":null,"maintainer_email":null,"license":"The MIT License (MIT)\n\nCopyright (c) 2026 Division of Medical Image Computing, German Cancer Research Center (DKFZ), Heidelberg, Germany\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n","classifier":["Development Status :: 4 - Beta","Framework :: napari","Intended Audience :: Developers","License :: OSI Approved :: MIT License","Operating System :: OS Independent","Programming Language :: Python","Programming Language :: Python :: 3","Programming Language :: Python :: 3 :: Only","Programming Language :: Python :: 3.10","Programming Language :: Python :: 3.11","Programming Language :: Python :: 3.12","Programming Language :: Python :: 3.13","Topic :: Scientific/Engineering :: Image Processing"],"requires_dist":["mlarray<2,>=1.0.0","napari-bboxes>=0.1.1","numpy","scipy","napari[all]>=0.6; extra == \"all\""],"requires_python":">=3.10","requires_external":null,"project_url":null,"provides_extra":["all"],"provides_dist":null,"obsoletes_dist":null},"npe1_shim":false}