Name | rlic JSON |
Version |
0.4.0
JSON |
| download |
home_page | None |
Summary | A minimal Line Integral Convolution extension for NumPy, written in Rust |
upload_time | 2025-07-10 12:19:52 |
maintainer | None |
docs_url | None |
author | C.M.T. Robert |
requires_python | >=3.9 |
license | MIT |
keywords |
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# rLIC
[](https://pypi.org/project/rlic/)
[](https://anaconda.org/conda-forge/rlic)
[](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[](https://pypi.org/project/rlic/)\n[](https://anaconda.org/conda-forge/rlic)\n[](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"
}