Name | rlic JSON |
Version |
0.5.1
JSON |
| download |
home_page | None |
Summary | A minimal Line Integral Convolution extension for NumPy, written in Rust |
upload_time | 2025-07-18 15:32:07 |
maintainer | None |
docs_url | None |
author | C.M.T. Robert |
requires_python | >=3.10 |
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.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[](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.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"
}