rlic


Namerlic JSON
Version 0.4.0 PyPI version JSON
download
home_pageNone
SummaryA minimal Line Integral Convolution extension for NumPy, written in Rust
upload_time2025-07-10 12:19:52
maintainerNone
docs_urlNone
authorC.M.T. Robert
requires_python>=3.9
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.4.0/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, 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.4.0/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)
    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.4.0/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.9",
    "maintainer_email": null,
    "keywords": null,
    "author": "C.M.T. Robert",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/1f/d5/96344e4c5dfeca8f03b10db4b854975f461e6d06beb7495d256ccf1b94aa/rlic-0.4.0.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.4.0/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(texture, U, V, kernel=kernel, iterations=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.4.0/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(texture, U, V, kernel=kernel, uv_mode=uv_mode)\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.4.0/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.4.0",
    "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": "ef2a7b6ab4ff981835d71580907bc21d0686dd0be7e418cc425325ee008b0c0b",
                "md5": "ec7a936356b13138cca35c9aa9e7411c",
                "sha256": "9931cb45c71bbffea8666315a8f98886a620dff04125bd610b68d846b3bff973"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl",
            "has_sig": false,
            "md5_digest": "ec7a936356b13138cca35c9aa9e7411c",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 201071,
            "upload_time": "2025-07-10T12:19:40",
            "upload_time_iso_8601": "2025-07-10T12:19:40.614900Z",
            "url": "https://files.pythonhosted.org/packages/ef/2a/7b6ab4ff981835d71580907bc21d0686dd0be7e418cc425325ee008b0c0b/rlic-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "65f52468ce9f87666309e9578aa700ce2925ac03987d7221c5aa47d976b03e64",
                "md5": "2a43af360dc5322eaf2cdbe6b4afad5c",
                "sha256": "bcf156df4c6610759cec1878e8cf935bf82d6d3d3581d91b8c04ba7f2f45509a"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "2a43af360dc5322eaf2cdbe6b4afad5c",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 190598,
            "upload_time": "2025-07-10T12:19:42",
            "upload_time_iso_8601": "2025-07-10T12:19:42.349446Z",
            "url": "https://files.pythonhosted.org/packages/65/f5/2468ce9f87666309e9578aa700ce2925ac03987d7221c5aa47d976b03e64/rlic-0.4.0-cp39-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "3661acda3baa0aa226156fe360f766d05f11854acbe5e709852422480908fe63",
                "md5": "888185b9510a3c456e7c7ce74f760f99",
                "sha256": "2977458a92d6d3f0ae8388e9999137b0709b50123dded6cb164d52b6ef7c608f"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-manylinux_2_28_aarch64.whl",
            "has_sig": false,
            "md5_digest": "888185b9510a3c456e7c7ce74f760f99",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 205585,
            "upload_time": "2025-07-10T12:19:44",
            "upload_time_iso_8601": "2025-07-10T12:19:44.021520Z",
            "url": "https://files.pythonhosted.org/packages/36/61/acda3baa0aa226156fe360f766d05f11854acbe5e709852422480908fe63/rlic-0.4.0-cp39-abi3-manylinux_2_28_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c2a426321df73baf862c83b2be8a17a74555673054eaa21e3bdf4073642dc8b7",
                "md5": "e77eee335cb7fccbfef5eafe6dceb9ee",
                "sha256": "bf287b621f4e18d8d93362300949f92b33fae9111b8ea4901b0101a267cc97bb"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-manylinux_2_28_x86_64.whl",
            "has_sig": false,
            "md5_digest": "e77eee335cb7fccbfef5eafe6dceb9ee",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 221106,
            "upload_time": "2025-07-10T12:19:46",
            "upload_time_iso_8601": "2025-07-10T12:19:46.036052Z",
            "url": "https://files.pythonhosted.org/packages/c2/a4/26321df73baf862c83b2be8a17a74555673054eaa21e3bdf4073642dc8b7/rlic-0.4.0-cp39-abi3-manylinux_2_28_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f7e268575b2b33b6822b1d4963057b0885789f65f46b1e8aba46b86b958442bf",
                "md5": "6200d443bbce98282bae306144887862",
                "sha256": "8d50ec75a89520851e6615e9005477439842e518ab698d7e3eab84362ba9922d"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-musllinux_1_2_aarch64.whl",
            "has_sig": false,
            "md5_digest": "6200d443bbce98282bae306144887862",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 385417,
            "upload_time": "2025-07-10T12:19:47",
            "upload_time_iso_8601": "2025-07-10T12:19:47.461859Z",
            "url": "https://files.pythonhosted.org/packages/f7/e2/68575b2b33b6822b1d4963057b0885789f65f46b1e8aba46b86b958442bf/rlic-0.4.0-cp39-abi3-musllinux_1_2_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a770319bc4aed4b42d54f9b828573197cc158efdf50a63ee6e4872f659583da7",
                "md5": "7dc23e6dd62ff3f828288ed8024e86db",
                "sha256": "00169b737bd7af475b3a794bea84c1b86e7ba97f58659b72f959f59ffd9612b7"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "7dc23e6dd62ff3f828288ed8024e86db",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 392488,
            "upload_time": "2025-07-10T12:19:48",
            "upload_time_iso_8601": "2025-07-10T12:19:48.830921Z",
            "url": "https://files.pythonhosted.org/packages/a7/70/319bc4aed4b42d54f9b828573197cc158efdf50a63ee6e4872f659583da7/rlic-0.4.0-cp39-abi3-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "565e6e7f1b34a249f4681659b0377c49b8296ed9a64dfd75189fa45c88519ba9",
                "md5": "307347c7aa86b50db0c75b3811662edb",
                "sha256": "2da8ce2164b8ca44e9b2d624b14aa808a2f7b6e4304cd81b91bf415d6539ef16"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-win32.whl",
            "has_sig": false,
            "md5_digest": "307347c7aa86b50db0c75b3811662edb",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 115919,
            "upload_time": "2025-07-10T12:19:50",
            "upload_time_iso_8601": "2025-07-10T12:19:50.479674Z",
            "url": "https://files.pythonhosted.org/packages/56/5e/6e7f1b34a249f4681659b0377c49b8296ed9a64dfd75189fa45c88519ba9/rlic-0.4.0-cp39-abi3-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "44a9bf3923c7c7e6cdb679af5cb5d87d6ef026fe79463a185929c72412ee3e32",
                "md5": "af9854642c48f01551ef8b87f116316c",
                "sha256": "be25c6d3a1c672e0458f6878cc726f7d162ec5dd68aa0ec05cfcab038bf1c5ff"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0-cp39-abi3-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "af9854642c48f01551ef8b87f116316c",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 121817,
            "upload_time": "2025-07-10T12:19:51",
            "upload_time_iso_8601": "2025-07-10T12:19:51.763245Z",
            "url": "https://files.pythonhosted.org/packages/44/a9/bf3923c7c7e6cdb679af5cb5d87d6ef026fe79463a185929c72412ee3e32/rlic-0.4.0-cp39-abi3-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1fd596344e4c5dfeca8f03b10db4b854975f461e6d06beb7495d256ccf1b94aa",
                "md5": "a6d4961ec242e2e541c579bbd2fc3fc7",
                "sha256": "d28b1efe64fbc3e28934457d8eb3f0fdb5431ac4dc33fc129a8a6642958cc406"
            },
            "downloads": -1,
            "filename": "rlic-0.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "a6d4961ec242e2e541c579bbd2fc3fc7",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 22647,
            "upload_time": "2025-07-10T12:19:52",
            "upload_time_iso_8601": "2025-07-10T12:19:52.897994Z",
            "url": "https://files.pythonhosted.org/packages/1f/d5/96344e4c5dfeca8f03b10db4b854975f461e6d06beb7495d256ccf1b94aa/rlic-0.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-10 12:19:52",
    "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: 0.45139s