rlic


Namerlic JSON
Version 0.5.1 PyPI version JSON
download
home_pageNone
SummaryA minimal Line Integral Convolution extension for NumPy, written in Rust
upload_time2025-07-18 15:32:07
maintainerNone
docs_urlNone
authorC.M.T. Robert
requires_python>=3.10
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # rLIC
[![PyPI](https://img.shields.io/pypi/v/rlic.svg?logo=pypi&logoColor=white&label=PyPI)](https://pypi.org/project/rlic/)
[![Conda Version](https://img.shields.io/conda/vn/conda-forge/rlic.svg?logo=condaforge&logoColor=white)](https://anaconda.org/conda-forge/rlic)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)

***Line Integral Convolution for Python, written in Rust***

`rLIC` (pronounced 'relic') is a highly optimized, minimal implementation of the
[Line Integral
Convolution](https://en.wikipedia.org/wiki/Line_integral_convolution) algorithm
for in-memory `numpy` arrays, written in Rust.


## Installation
```
python -m pip install rLIC
```

## Examples

`rLIC` consists in a single Python function, `rlic.convolve`, that convolves a
`texture` image (usually noise) with a 2D vector field described by its
components `u` and `v`, via a 1D `kernel` array. The result is an image where
pixel intensity is strongly correlated along field lines.

Let's see an example. We'll use `matplotlib` to visualize inputs and outputs.
```py
import matplotlib.pyplot as plt
import numpy as np

import rlic

SHAPE = NX, NY = (256, 256)
prng = np.random.default_rng(0)

texture = prng.random(SHAPE)
x = np.linspace(0, np.pi, NY)
U = np.broadcast_to(np.cos(2 * x), SHAPE)
V = np.broadcast_to(np.sin(x).T, SHAPE)

fig, axs = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(10, 5))
for ax in axs:
    ax.set(aspect="equal", xticks=[], yticks=[])

ax = axs[0]
ax.set_title("Input texture (noise)")
ax.imshow(texture)

ax = axs[1]
ax.set_title("Input vector field")
Y, X = np.mgrid[0:NY, 0:NX]
ax.streamplot(X, Y, U, V)
```
<p align="center">
<a href="https://github.com/neutrinoceros/rlic">
<img src="https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/base_example_in.png" width="600"></a>
</p>

Now let's compute some convolutions, varying the number of iterations
```py
kernel = 1 - np.abs(np.linspace(-1, 1, 65))

fig_out, axs_out = plt.subplots(ncols=3, figsize=(15, 5))
for ax in axs_out:
    ax.set(aspect="equal", xticks=[], yticks=[])
for n, ax in zip((1, 5, 100), axs_out, strict=True):
    image = rlic.convolve(
        texture,
        U,
        V,
        kernel=kernel,
        boundaries="periodic",
        iterations=n,
    )
    ax.set_title(f"Convolution result ({n} iteration(s))")
    ax.imshow(image)
```
<p align="center">
<a href="https://github.com/neutrinoceros/rlic">
<img src="https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/base_example_out.png" width="900"></a>
</p>

## Polarization mode

By default, the direction of the vector field affects the result. That is, the
*sign* of each component matters. Such a vector field is analogous to a velocity
field. However, the sign of `u` or `v` may sometimes be irrelevant, and only
their absolute directions should be taken into account. Such a vector field is
analogous to a polarization field. `rLIC` supports this use case via an
additional keyword argument, `uv_mode`, which can be either `'velocity'`
(default), or `'polarization'`. In practice, the difference between these two
modes in only visible around sharps changes in sign in either `u` or `v`, and
with certain kernels.
Let's illustrate one such case

```py
import matplotlib.pyplot as plt
import numpy as np

import rlic

SHAPE = NX, NY = (256, 256)
prng = np.random.default_rng(0)

texture = prng.random(SHAPE)
kernel = 1 - np.abs(np.linspace(-1, 1, 65, dtype="float64"))

U0 = np.ones(SHAPE)
ii = np.broadcast_to(np.arange(NX), SHAPE)
U = np.where(ii<NX/2, -U0, U0)
V = np.zeros((NX, NX))

fig, axs = plt.subplots(ncols=3, sharex=True, sharey=True, figsize=(15, 5))
for ax in axs:
    ax.set(aspect="equal", xticks=[], yticks=[])

ax = axs[0]
ax.set_title("Input vector field")
Y, X = np.mgrid[0:NY, 0:NX]
ax.streamplot(X, Y, U, V)

for uv_mode, ax in zip(("velocity", "polarization"), axs[1:], strict=True):
    image = rlic.convolve(
        texture,
        U,
        V,
        kernel=kernel,
        uv_mode=uv_mode,
        boundaries={"x": "periodic", "y": "closed"},
    )
    ax.set_title(f"{uv_mode=!r}")
    ax.imshow(image)
```

<p align="center">
<a href="https://github.com/neutrinoceros/rlic">
<img src="https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/polarization_example.png" width="900"></a>
</p>


## Memory Usage

`rLIC.convolve` allocates exactly two buffers with the same size as `texture`,
`u` and `v`, regardless of the number of `iterations` performed, one of which is
discarded when the function returns. This means that peak usage is about 5/3 of
the amount needed to hold input data in memory, and usage drops to 4/3 on
return.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "rlic",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": null,
    "author": "C.M.T. Robert",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/f1/24/d739ddda19b20df0b6148482ecd9a86f3031ea76997d1a9ca79da781a83b/rlic-0.5.1.tar.gz",
    "platform": null,
    "description": "# rLIC\n[![PyPI](https://img.shields.io/pypi/v/rlic.svg?logo=pypi&logoColor=white&label=PyPI)](https://pypi.org/project/rlic/)\n[![Conda Version](https://img.shields.io/conda/vn/conda-forge/rlic.svg?logo=condaforge&logoColor=white)](https://anaconda.org/conda-forge/rlic)\n[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)\n\n***Line Integral Convolution for Python, written in Rust***\n\n`rLIC` (pronounced 'relic') is a highly optimized, minimal implementation of the\n[Line Integral\nConvolution](https://en.wikipedia.org/wiki/Line_integral_convolution) algorithm\nfor in-memory `numpy` arrays, written in Rust.\n\n\n## Installation\n```\npython -m pip install rLIC\n```\n\n## Examples\n\n`rLIC` consists in a single Python function, `rlic.convolve`, that convolves a\n`texture` image (usually noise) with a 2D vector field described by its\ncomponents `u` and `v`, via a 1D `kernel` array. The result is an image where\npixel intensity is strongly correlated along field lines.\n\nLet's see an example. We'll use `matplotlib` to visualize inputs and outputs.\n```py\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nimport rlic\n\nSHAPE = NX, NY = (256, 256)\nprng = np.random.default_rng(0)\n\ntexture = prng.random(SHAPE)\nx = np.linspace(0, np.pi, NY)\nU = np.broadcast_to(np.cos(2 * x), SHAPE)\nV = np.broadcast_to(np.sin(x).T, SHAPE)\n\nfig, axs = plt.subplots(ncols=2, sharex=True, sharey=True, figsize=(10, 5))\nfor ax in axs:\n    ax.set(aspect=\"equal\", xticks=[], yticks=[])\n\nax = axs[0]\nax.set_title(\"Input texture (noise)\")\nax.imshow(texture)\n\nax = axs[1]\nax.set_title(\"Input vector field\")\nY, X = np.mgrid[0:NY, 0:NX]\nax.streamplot(X, Y, U, V)\n```\n<p align=\"center\">\n<a href=\"https://github.com/neutrinoceros/rlic\">\n<img src=\"https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/base_example_in.png\" width=\"600\"></a>\n</p>\n\nNow let's compute some convolutions, varying the number of iterations\n```py\nkernel = 1 - np.abs(np.linspace(-1, 1, 65))\n\nfig_out, axs_out = plt.subplots(ncols=3, figsize=(15, 5))\nfor ax in axs_out:\n    ax.set(aspect=\"equal\", xticks=[], yticks=[])\nfor n, ax in zip((1, 5, 100), axs_out, strict=True):\n    image = rlic.convolve(\n        texture,\n        U,\n        V,\n        kernel=kernel,\n        boundaries=\"periodic\",\n        iterations=n,\n    )\n    ax.set_title(f\"Convolution result ({n} iteration(s))\")\n    ax.imshow(image)\n```\n<p align=\"center\">\n<a href=\"https://github.com/neutrinoceros/rlic\">\n<img src=\"https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/base_example_out.png\" width=\"900\"></a>\n</p>\n\n## Polarization mode\n\nBy default, the direction of the vector field affects the result. That is, the\n*sign* of each component matters. Such a vector field is analogous to a velocity\nfield. However, the sign of `u` or `v` may sometimes be irrelevant, and only\ntheir absolute directions should be taken into account. Such a vector field is\nanalogous to a polarization field. `rLIC` supports this use case via an\nadditional keyword argument, `uv_mode`, which can be either `'velocity'`\n(default), or `'polarization'`. In practice, the difference between these two\nmodes in only visible around sharps changes in sign in either `u` or `v`, and\nwith certain kernels.\nLet's illustrate one such case\n\n```py\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nimport rlic\n\nSHAPE = NX, NY = (256, 256)\nprng = np.random.default_rng(0)\n\ntexture = prng.random(SHAPE)\nkernel = 1 - np.abs(np.linspace(-1, 1, 65, dtype=\"float64\"))\n\nU0 = np.ones(SHAPE)\nii = np.broadcast_to(np.arange(NX), SHAPE)\nU = np.where(ii<NX/2, -U0, U0)\nV = np.zeros((NX, NX))\n\nfig, axs = plt.subplots(ncols=3, sharex=True, sharey=True, figsize=(15, 5))\nfor ax in axs:\n    ax.set(aspect=\"equal\", xticks=[], yticks=[])\n\nax = axs[0]\nax.set_title(\"Input vector field\")\nY, X = np.mgrid[0:NY, 0:NX]\nax.streamplot(X, Y, U, V)\n\nfor uv_mode, ax in zip((\"velocity\", \"polarization\"), axs[1:], strict=True):\n    image = rlic.convolve(\n        texture,\n        U,\n        V,\n        kernel=kernel,\n        uv_mode=uv_mode,\n        boundaries={\"x\": \"periodic\", \"y\": \"closed\"},\n    )\n    ax.set_title(f\"{uv_mode=!r}\")\n    ax.imshow(image)\n```\n\n<p align=\"center\">\n<a href=\"https://github.com/neutrinoceros/rlic\">\n<img src=\"https://raw.githubusercontent.com/neutrinoceros/rlic/v0.5.1/static/polarization_example.png\" width=\"900\"></a>\n</p>\n\n\n## Memory Usage\n\n`rLIC.convolve` allocates exactly two buffers with the same size as `texture`,\n`u` and `v`, regardless of the number of `iterations` performed, one of which is\ndiscarded when the function returns. This means that peak usage is about 5/3 of\nthe amount needed to hold input data in memory, and usage drops to 4/3 on\nreturn.\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A minimal Line Integral Convolution extension for NumPy, written in Rust",
    "version": "0.5.1",
    "project_urls": {
        "Changelog": "https://github.com/neutrinoceros/rlic/blob/main/CHANGELOG.md",
        "Homepage": "https://github.com/neutrinoceros/rlic"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ba32db61c714e24fe8d70fceafe128030b57d03b6dee194baf7a151a25e1e2d7",
                "md5": "4b9e5590d3fc69f7ce3af04cd64fa123",
                "sha256": "12a49f2e68b6bf392d5ba1d000237fdfa4dc0119f02689961968072119b4b0e4"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl",
            "has_sig": false,
            "md5_digest": "4b9e5590d3fc69f7ce3af04cd64fa123",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 205214,
            "upload_time": "2025-07-18T15:31:57",
            "upload_time_iso_8601": "2025-07-18T15:31:57.012390Z",
            "url": "https://files.pythonhosted.org/packages/ba/32/db61c714e24fe8d70fceafe128030b57d03b6dee194baf7a151a25e1e2d7/rlic-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0f26b65049a2f25d2ff619f8bf70d8e86ebedb3c75a24fabe1dd67566403a002",
                "md5": "681f9b829df4244ce3f2ebe129943c6f",
                "sha256": "327ddf1c99a7862bc092a18ba0d07834194ca6365b97b3a940a04769a9b575a1"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "681f9b829df4244ce3f2ebe129943c6f",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 193748,
            "upload_time": "2025-07-18T15:31:58",
            "upload_time_iso_8601": "2025-07-18T15:31:58.262331Z",
            "url": "https://files.pythonhosted.org/packages/0f/26/b65049a2f25d2ff619f8bf70d8e86ebedb3c75a24fabe1dd67566403a002/rlic-0.5.1-cp310-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a92e35092850a033c3d2205b35582a8b1ff9342a805f3b9d4868ae132dd96e7c",
                "md5": "dd09e0993274982c4857c0b0e88301c0",
                "sha256": "cdd3d5465f4e6eb917b23748053debc0ca94873f696e08ef95549a677ed981ea"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl",
            "has_sig": false,
            "md5_digest": "dd09e0993274982c4857c0b0e88301c0",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 209220,
            "upload_time": "2025-07-18T15:31:59",
            "upload_time_iso_8601": "2025-07-18T15:31:59.346940Z",
            "url": "https://files.pythonhosted.org/packages/a9/2e/35092850a033c3d2205b35582a8b1ff9342a805f3b9d4868ae132dd96e7c/rlic-0.5.1-cp310-abi3-manylinux_2_28_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b0fbc490c1780e034839bc4fe2642a50b7084155f5e4043a67bf797218625891",
                "md5": "57ea8adf1d75fda052634f3429eab06b",
                "sha256": "e4576dc2b142039df545a4f140e391ea2f91bf66ec1ab4dd25bb5391bd6ed219"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "57ea8adf1d75fda052634f3429eab06b",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 224999,
            "upload_time": "2025-07-18T15:32:01",
            "upload_time_iso_8601": "2025-07-18T15:32:01.167738Z",
            "url": "https://files.pythonhosted.org/packages/b0/fb/c490c1780e034839bc4fe2642a50b7084155f5e4043a67bf797218625891/rlic-0.5.1-cp310-abi3-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "86340af4a8500ade097390292615f6c6cc2fbae93aa0fb04385ba3d1ca75f5db",
                "md5": "b8d0ad40a470b1c33b002c1e4e7fad35",
                "sha256": "341d3848a7e927a13e4a0c0cc641a9aae8e35dddc307676be41a040de07ea842"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-musllinux_1_2_aarch64.whl",
            "has_sig": false,
            "md5_digest": "b8d0ad40a470b1c33b002c1e4e7fad35",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 389047,
            "upload_time": "2025-07-18T15:32:02",
            "upload_time_iso_8601": "2025-07-18T15:32:02.586789Z",
            "url": "https://files.pythonhosted.org/packages/86/34/0af4a8500ade097390292615f6c6cc2fbae93aa0fb04385ba3d1ca75f5db/rlic-0.5.1-cp310-abi3-musllinux_1_2_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d07bf7e9cdee39a6117b6921fe8b155abe9b74be46408a1ab886a465aa9638a2",
                "md5": "0a400d72910b576c602c33b7e189578f",
                "sha256": "0fd17b8b698cfd8c5e9a92589bf63e8f8f766a44556f7dc39f73a48e0febe50a"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "0a400d72910b576c602c33b7e189578f",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 396247,
            "upload_time": "2025-07-18T15:32:03",
            "upload_time_iso_8601": "2025-07-18T15:32:03.736951Z",
            "url": "https://files.pythonhosted.org/packages/d0/7b/f7e9cdee39a6117b6921fe8b155abe9b74be46408a1ab886a465aa9638a2/rlic-0.5.1-cp310-abi3-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "608be0be1398b4394ebfb493bf4ff0782c31b3a58b28a48cf8fa6e470fb37fa9",
                "md5": "c603f0b3c88c705e1842c554146f164f",
                "sha256": "431ca8ee4e280bec7235484097d62543f5e678dbf60a26951037a4068a064822"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-win32.whl",
            "has_sig": false,
            "md5_digest": "c603f0b3c88c705e1842c554146f164f",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 119612,
            "upload_time": "2025-07-18T15:32:05",
            "upload_time_iso_8601": "2025-07-18T15:32:05.018328Z",
            "url": "https://files.pythonhosted.org/packages/60/8b/e0be1398b4394ebfb493bf4ff0782c31b3a58b28a48cf8fa6e470fb37fa9/rlic-0.5.1-cp310-abi3-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "26ca1f55d55e1bb7b1ed5ed61ccc8783208e2b014457de2cf28af1d5dc9a2614",
                "md5": "e17226299ec9cf58ae92054cd58c1436",
                "sha256": "7245899ea525b7837743381daf54b1f6be93b4c9d7fcfe6de9bc2b4146ce5708"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1-cp310-abi3-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "e17226299ec9cf58ae92054cd58c1436",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.10",
            "size": 123751,
            "upload_time": "2025-07-18T15:32:06",
            "upload_time_iso_8601": "2025-07-18T15:32:06.099453Z",
            "url": "https://files.pythonhosted.org/packages/26/ca/1f55d55e1bb7b1ed5ed61ccc8783208e2b014457de2cf28af1d5dc9a2614/rlic-0.5.1-cp310-abi3-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f124d739ddda19b20df0b6148482ecd9a86f3031ea76997d1a9ca79da781a83b",
                "md5": "c1af8d4546e20947a3a31a607463a58d",
                "sha256": "d965cc96ff81c6500db259af82c6e1acde73a5d7ce46affcccacf2b6cc17881d"
            },
            "downloads": -1,
            "filename": "rlic-0.5.1.tar.gz",
            "has_sig": false,
            "md5_digest": "c1af8d4546e20947a3a31a607463a58d",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 25666,
            "upload_time": "2025-07-18T15:32:07",
            "upload_time_iso_8601": "2025-07-18T15:32:07.206342Z",
            "url": "https://files.pythonhosted.org/packages/f1/24/d739ddda19b20df0b6148482ecd9a86f3031ea76997d1a9ca79da781a83b/rlic-0.5.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-18 15:32:07",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "neutrinoceros",
    "github_project": "rlic",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "rlic"
}
        
Elapsed time: 2.40138s