normal-grain-merge


Namenormal-grain-merge JSON
Version 0.0.1 PyPI version JSON
download
home_pageNone
SummaryFused normal and grain merge C extension
upload_time2025-09-07 03:05:30
maintainerNone
docs_urlNone
authorSamuel Howard
requires_python>=3.8
licenseMIT
keywords image processing
VCS
bugtrack_url
requirements numpy opencv-python
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # normal_grain_merge

This implements a combined version of the blend modes normal and grain merge.
Grain merge is performed on *s* and *t* with the result normal-merged with *b*.
Subscripts indicate channels, with alpha (α) channels broadcast to three channels.

$$
(((\mathrm{t_{rgb}} + \mathrm{s_{rgb}} - 0.5) * \mathrm{t_\alpha} + \mathrm{t_{rgb}} * (1 - \mathrm{t_\alpha})) * (1 - 0.3) + \mathrm{s_{rgb}} * 0.3) * \mathrm{t_\alpha} + \mathrm{b_{rgb}} * (1 - \mathrm{t_\alpha})
$$

## Usage
```py
import numpy as np
from normal_grain_merge import normal_grain_merge, KernelKind


# Example arrays
base = np.zeros((100, 100, 3), dtype=np.uint8)
texture = np.zeros((100, 100, 3), dtype=np.uint8)
skin = np.zeros((100, 100, 4), dtype=np.uint8)
im_alpha = np.zeros((100, 100), dtype=np.uint8)

result_scalar = normal_grain_merge(base, texture, skin, im_alpha, KernelKind.KERNEL_SCALAR.value)
print(result_scalar.shape, result_scalar.dtype)
```

There are three kernels implemented in this module as defined in `KernelKind`.

- `KERNEL_AUTO`: Automatically chooses the kernel, preferring AVX2
- `KERNEL_SCALAR`: Portable scalar implementation.
- `KERNEL_SSE42`: SSE4.2 intrinsics kernel. Likely better on AMD CPUs.
- `KERNEL_AVX2`: AVX2 intrinsics kernel. Likely better on Intel CPUs.

### Parameters

All input matrices should have the same height and width.

#### `base`

RGB or RGBA, dropping the alpha channel if it exists.
The base image for application.

#### `texture`

RGB or RGBA, applying the alpha if it exists.
This is the texture to be applied.

#### `skin`

RGBA, the segmented portion of base to texture.
The "skin" of the object the texture is to be applied to.

#### `im_alpha`

The alpha of parameter `skin`. 
This is mostly a holdover from the Python implementation to deal with NumPy.

#### `kernel`

One of `KernelKind`.

## Performance

The entire reason for me writing this was NumPy being slow when this operation is in the hot path.
So, I decided to write a SIMD version that does the type casting outside NumPy with only the intermediate values being in FP32.

How much of a speedup is this? All numbers are from a Ryzen 7 4800H running Windows 11 and Python 3.12.4.

| Method/Kernel     | Average Iteration Time |
|-------------------|------------------------|
| C scalar kernel   | 0.019565s              |
| C SSE4.2 kernel   | 0.013705s              |
| C AVX2 kernel     | 0.016842s              |
| NumPy version     | 0.228098s              |
| Old NumPy version | 0.350554s              |

| Method Comparison  | Speedup  |
|--------------------|----------|
| NumPy -> scalar    | 91.4227% |
| NumPy -> SSE4.2    | 93.9915% |
| NumPy -> AVX2      | 92.6165% |
| Old np -> SSE4.2   | 96.0904% |
| C scalar -> SSE4.2 | 29.9487% |
| C scalar -> AVX2   | 13.9183% |

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "normal-grain-merge",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "image, processing",
    "author": "Samuel Howard",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/03/7d/08aaf3a067d5757090b6d9cef0ae8f1c73b885a9768aadf2ae659468f2a9/normal_grain_merge-0.0.1.tar.gz",
    "platform": null,
    "description": "# normal_grain_merge\n\nThis implements a combined version of the blend modes normal and grain merge.\nGrain merge is performed on *s* and *t* with the result normal-merged with *b*.\nSubscripts indicate channels, with alpha (\u03b1) channels broadcast to three channels.\n\n$$\n(((\\mathrm{t_{rgb}} + \\mathrm{s_{rgb}} - 0.5) * \\mathrm{t_\\alpha} + \\mathrm{t_{rgb}} * (1 - \\mathrm{t_\\alpha})) * (1 - 0.3) + \\mathrm{s_{rgb}} * 0.3) * \\mathrm{t_\\alpha} + \\mathrm{b_{rgb}} * (1 - \\mathrm{t_\\alpha})\n$$\n\n## Usage\n```py\nimport numpy as np\nfrom normal_grain_merge import normal_grain_merge, KernelKind\n\n\n# Example arrays\nbase = np.zeros((100, 100, 3), dtype=np.uint8)\ntexture = np.zeros((100, 100, 3), dtype=np.uint8)\nskin = np.zeros((100, 100, 4), dtype=np.uint8)\nim_alpha = np.zeros((100, 100), dtype=np.uint8)\n\nresult_scalar = normal_grain_merge(base, texture, skin, im_alpha, KernelKind.KERNEL_SCALAR.value)\nprint(result_scalar.shape, result_scalar.dtype)\n```\n\nThere are three kernels implemented in this module as defined in `KernelKind`.\n\n- `KERNEL_AUTO`: Automatically chooses the kernel, preferring AVX2\n- `KERNEL_SCALAR`: Portable scalar implementation.\n- `KERNEL_SSE42`: SSE4.2 intrinsics kernel. Likely better on AMD CPUs.\n- `KERNEL_AVX2`: AVX2 intrinsics kernel. Likely better on Intel CPUs.\n\n### Parameters\n\nAll input matrices should have the same height and width.\n\n#### `base`\n\nRGB or RGBA, dropping the alpha channel if it exists.\nThe base image for application.\n\n#### `texture`\n\nRGB or RGBA, applying the alpha if it exists.\nThis is the texture to be applied.\n\n#### `skin`\n\nRGBA, the segmented portion of base to texture.\nThe \"skin\" of the object the texture is to be applied to.\n\n#### `im_alpha`\n\nThe alpha of parameter `skin`. \nThis is mostly a holdover from the Python implementation to deal with NumPy.\n\n#### `kernel`\n\nOne of `KernelKind`.\n\n## Performance\n\nThe entire reason for me writing this was NumPy being slow when this operation is in the hot path.\nSo, I decided to write a SIMD version that does the type casting outside NumPy with only the intermediate values being in FP32.\n\nHow much of a speedup is this? All numbers are from a Ryzen 7 4800H running Windows 11 and Python 3.12.4.\n\n| Method/Kernel     | Average Iteration Time |\n|-------------------|------------------------|\n| C scalar kernel   | 0.019565s              |\n| C SSE4.2 kernel   | 0.013705s              |\n| C AVX2 kernel     | 0.016842s              |\n| NumPy version     | 0.228098s              |\n| Old NumPy version | 0.350554s              |\n\n| Method Comparison  | Speedup  |\n|--------------------|----------|\n| NumPy -> scalar    | 91.4227% |\n| NumPy -> SSE4.2    | 93.9915% |\n| NumPy -> AVX2      | 92.6165% |\n| Old np -> SSE4.2   | 96.0904% |\n| C scalar -> SSE4.2 | 29.9487% |\n| C scalar -> AVX2   | 13.9183% |\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Fused normal and grain merge C extension",
    "version": "0.0.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/samhaswon/normal_grain_merge/issues",
        "Homepage": "https://github.com/samhaswon/normal_grain_merge"
    },
    "split_keywords": [
        "image",
        " processing"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "74eedc414bcc8acd95cb412c02d59f746c1ae5175a919efa7e0ea12897e36670",
                "md5": "b6c462c17e4b8a5612c5fd788b15095d",
                "sha256": "da8867d1e5c02184e4d9000a7db969dbb4651824d63407db7b63baa9f73ffdf6"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "b6c462c17e4b8a5612c5fd788b15095d",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 71990,
            "upload_time": "2025-09-07T03:05:00",
            "upload_time_iso_8601": "2025-09-07T03:05:00.259486Z",
            "url": "https://files.pythonhosted.org/packages/74/ee/dc414bcc8acd95cb412c02d59f746c1ae5175a919efa7e0ea12897e36670/normal_grain_merge-0.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "dab06166b7e1866f777aa5ada0d005c05b16e9f6dd987e7d1305ffa8d0665ffe",
                "md5": "a85392494dea7500817017194c9f202e",
                "sha256": "0d4dc76792585e3dba69f625cf8dca077bfeab7d8a0cfaa290305b11f0a8575f"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "a85392494dea7500817017194c9f202e",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 70050,
            "upload_time": "2025-09-07T03:05:01",
            "upload_time_iso_8601": "2025-09-07T03:05:01.386890Z",
            "url": "https://files.pythonhosted.org/packages/da/b0/6166b7e1866f777aa5ada0d005c05b16e9f6dd987e7d1305ffa8d0665ffe/normal_grain_merge-0.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "212990090d15ef053e564ffe37a3c14c03523d0f1503bffc34827e13b04d8fa0",
                "md5": "9c5fac1c5429b4e6ef5f78ad2433f937",
                "sha256": "6cb4f20b697ae62deb2755d9f2c76db6e6a76d0841eb712c140dac46b792e736"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp310-cp310-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "9c5fac1c5429b4e6ef5f78ad2433f937",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 71757,
            "upload_time": "2025-09-07T03:05:02",
            "upload_time_iso_8601": "2025-09-07T03:05:02.442227Z",
            "url": "https://files.pythonhosted.org/packages/21/29/90090d15ef053e564ffe37a3c14c03523d0f1503bffc34827e13b04d8fa0/normal_grain_merge-0.0.1-cp310-cp310-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b665f74fa20ac71ad8f98456aded962e4019cf07580c284eb36a18961f82483f",
                "md5": "4d32d761cda2fc0bf527966bd4d38543",
                "sha256": "6a6c5b98fa90611d89dda1de149a84ed2d05dea499c1f47030c8639f7e279faf"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp310-cp310-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "4d32d761cda2fc0bf527966bd4d38543",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 71638,
            "upload_time": "2025-09-07T03:05:03",
            "upload_time_iso_8601": "2025-09-07T03:05:03.194181Z",
            "url": "https://files.pythonhosted.org/packages/b6/65/f74fa20ac71ad8f98456aded962e4019cf07580c284eb36a18961f82483f/normal_grain_merge-0.0.1-cp310-cp310-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d489e164e4b21d57729189a4723c3dd8539364d8a232f5e50fbed356a4480913",
                "md5": "edbd7dcf280a57fb3577ef137607e07a",
                "sha256": "652dd0f2b61c41f6d4675e248e3f2d9ed3163745ee746ebc3aabcec9c529549a"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp310-cp310-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "edbd7dcf280a57fb3577ef137607e07a",
            "packagetype": "bdist_wheel",
            "python_version": "cp310",
            "requires_python": ">=3.8",
            "size": 22825,
            "upload_time": "2025-09-07T03:05:04",
            "upload_time_iso_8601": "2025-09-07T03:05:04.232113Z",
            "url": "https://files.pythonhosted.org/packages/d4/89/e164e4b21d57729189a4723c3dd8539364d8a232f5e50fbed356a4480913/normal_grain_merge-0.0.1-cp310-cp310-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fd4b4bf49b4659e1cb885081f46069b13b9b4332ccb55115804276884c028e7f",
                "md5": "0e60d8d72f56f393660022c7e31ac2a5",
                "sha256": "6ce384026629e08315fbc905cb33ff7a505e5098cd932fcf00eda9eb5ceee911"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "0e60d8d72f56f393660022c7e31ac2a5",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 72850,
            "upload_time": "2025-09-07T03:05:04",
            "upload_time_iso_8601": "2025-09-07T03:05:04.921343Z",
            "url": "https://files.pythonhosted.org/packages/fd/4b/4bf49b4659e1cb885081f46069b13b9b4332ccb55115804276884c028e7f/normal_grain_merge-0.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ed24fc69fa3899d62f521fed1d6afe9906143bbf771df4488cd0c5db33e43fd2",
                "md5": "9063341cc17d5315b2909a0a80bcb86d",
                "sha256": "df505a37f0964cc7fa9fadcdbb201cfa7a8def68253bc028e134517c728bc2d0"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "9063341cc17d5315b2909a0a80bcb86d",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 70835,
            "upload_time": "2025-09-07T03:05:06",
            "upload_time_iso_8601": "2025-09-07T03:05:06.390030Z",
            "url": "https://files.pythonhosted.org/packages/ed/24/fc69fa3899d62f521fed1d6afe9906143bbf771df4488cd0c5db33e43fd2/normal_grain_merge-0.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c1d2e59293dcf871901547c1f68c6a621575b1473e030d7570c6529a38f6366e",
                "md5": "af406a39a877a59b4231110e2bf328d6",
                "sha256": "b00282f8610612ff00e41c76ca17725a4191cec043264f37f86d72fd86b2e62f"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp311-cp311-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "af406a39a877a59b4231110e2bf328d6",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 72555,
            "upload_time": "2025-09-07T03:05:07",
            "upload_time_iso_8601": "2025-09-07T03:05:07.427263Z",
            "url": "https://files.pythonhosted.org/packages/c1/d2/e59293dcf871901547c1f68c6a621575b1473e030d7570c6529a38f6366e/normal_grain_merge-0.0.1-cp311-cp311-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c161127ab2d5e154c8874b4fa061b6531320c78ddef626377a95e48aae565510",
                "md5": "ee38cffaef126b0ea34b148cb631d327",
                "sha256": "c3f6a2264c4eb34cc8b5aa1ef7c544979433ddc11cd71801dba4745f6d07f8c8"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp311-cp311-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "ee38cffaef126b0ea34b148cb631d327",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 72431,
            "upload_time": "2025-09-07T03:05:08",
            "upload_time_iso_8601": "2025-09-07T03:05:08.474020Z",
            "url": "https://files.pythonhosted.org/packages/c1/61/127ab2d5e154c8874b4fa061b6531320c78ddef626377a95e48aae565510/normal_grain_merge-0.0.1-cp311-cp311-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "0a4124e1408e13b2b251c7e7714aa8ba636c6ff9de5583ba1e0f1bbef8e79b92",
                "md5": "c94a6a5a130cdcfb9b9d1f574708622d",
                "sha256": "7872b69228b2c52fd10dacf340cef7e8ba839bc8d8458e6ae7b7dd28b313ceef"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp311-cp311-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "c94a6a5a130cdcfb9b9d1f574708622d",
            "packagetype": "bdist_wheel",
            "python_version": "cp311",
            "requires_python": ">=3.8",
            "size": 22821,
            "upload_time": "2025-09-07T03:05:09",
            "upload_time_iso_8601": "2025-09-07T03:05:09.851624Z",
            "url": "https://files.pythonhosted.org/packages/0a/41/24e1408e13b2b251c7e7714aa8ba636c6ff9de5583ba1e0f1bbef8e79b92/normal_grain_merge-0.0.1-cp311-cp311-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8cca45e9026940c5114f4caae01afeeb0c4cb39e28c2c294b4bf799785a2a901",
                "md5": "2a3590b5958943f914eea4c44e799e0e",
                "sha256": "764d508aa4ebcb7dd5c912686ddf321024775bd4502ebba7404889131dd950b8"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "2a3590b5958943f914eea4c44e799e0e",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 73084,
            "upload_time": "2025-09-07T03:05:10",
            "upload_time_iso_8601": "2025-09-07T03:05:10.861567Z",
            "url": "https://files.pythonhosted.org/packages/8c/ca/45e9026940c5114f4caae01afeeb0c4cb39e28c2c294b4bf799785a2a901/normal_grain_merge-0.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "014492f93b98dd7fde46864e94dc11447d6058ce02eed6fabe63d0f08f85e4e1",
                "md5": "a5351f03d155097fb63fd97c2c68cdc6",
                "sha256": "8071b1ca46e0951b6eca5d697b5028df40a5f998e9140e148bd5e68d9ac66f2f"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "a5351f03d155097fb63fd97c2c68cdc6",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 71049,
            "upload_time": "2025-09-07T03:05:11",
            "upload_time_iso_8601": "2025-09-07T03:05:11.574913Z",
            "url": "https://files.pythonhosted.org/packages/01/44/92f93b98dd7fde46864e94dc11447d6058ce02eed6fabe63d0f08f85e4e1/normal_grain_merge-0.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a959cd3b71bdab3f0ba4ff91a2ce8d0cb5db4e98dac8e4a4327b1bf15055d74c",
                "md5": "f5c6d200eb7aa2360f3434f84c0d34c8",
                "sha256": "29ada5ccb7f787f87f8b2f52713d59b37c85c7ca4cab9134c352346ef2e64023"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp312-cp312-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "f5c6d200eb7aa2360f3434f84c0d34c8",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 72089,
            "upload_time": "2025-09-07T03:05:12",
            "upload_time_iso_8601": "2025-09-07T03:05:12.516406Z",
            "url": "https://files.pythonhosted.org/packages/a9/59/cd3b71bdab3f0ba4ff91a2ce8d0cb5db4e98dac8e4a4327b1bf15055d74c/normal_grain_merge-0.0.1-cp312-cp312-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "df58bd4f88d10733a3fb083256960da2d987ddd8b90e20ed7263b114d4c4f26b",
                "md5": "b52027d9d80100a180666f4c3b42bce9",
                "sha256": "035d04328ef5617a678fad2b112a3db0057eec6a4bd321fbba71ee1f061b47b4"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp312-cp312-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "b52027d9d80100a180666f4c3b42bce9",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 73757,
            "upload_time": "2025-09-07T03:05:13",
            "upload_time_iso_8601": "2025-09-07T03:05:13.598950Z",
            "url": "https://files.pythonhosted.org/packages/df/58/bd4f88d10733a3fb083256960da2d987ddd8b90e20ed7263b114d4c4f26b/normal_grain_merge-0.0.1-cp312-cp312-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "bcf9234777644577d478fccb34462d8b79f9eec9fc94ec0b5d5b85b49d8726de",
                "md5": "fa0baf94a1836c09a5b2171b379ad99c",
                "sha256": "6eb22d7c7e9f39198659f207a479372f1ad447fd5a2bd3bebfd075f72e201f88"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp312-cp312-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "fa0baf94a1836c09a5b2171b379ad99c",
            "packagetype": "bdist_wheel",
            "python_version": "cp312",
            "requires_python": ">=3.8",
            "size": 22874,
            "upload_time": "2025-09-07T03:05:14",
            "upload_time_iso_8601": "2025-09-07T03:05:14.614282Z",
            "url": "https://files.pythonhosted.org/packages/bc/f9/234777644577d478fccb34462d8b79f9eec9fc94ec0b5d5b85b49d8726de/normal_grain_merge-0.0.1-cp312-cp312-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "9d1c8ef0baf4d77a7e229d0178c700488eacf8bb84c05868db01de3b449ab87b",
                "md5": "8a91e16d304231f12561ff115eac2025",
                "sha256": "116bd04e0445a95a3f697892122bfd61364d9a254b309432dada6aa9d7023818"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "8a91e16d304231f12561ff115eac2025",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8",
            "size": 73102,
            "upload_time": "2025-09-07T03:05:15",
            "upload_time_iso_8601": "2025-09-07T03:05:15.323153Z",
            "url": "https://files.pythonhosted.org/packages/9d/1c/8ef0baf4d77a7e229d0178c700488eacf8bb84c05868db01de3b449ab87b/normal_grain_merge-0.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b78b7ab8557ff4eb7dd04326631e3caa1029bca8c2d99da5edce047040a4fd00",
                "md5": "0b36f4823bd26627e0d4793a2f100ff4",
                "sha256": "ff2be18531d6ebaa6a2dbf92b8aa076f279ec4dcc0fef76b0471d56857618363"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "0b36f4823bd26627e0d4793a2f100ff4",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8",
            "size": 71044,
            "upload_time": "2025-09-07T03:05:16",
            "upload_time_iso_8601": "2025-09-07T03:05:16.338590Z",
            "url": "https://files.pythonhosted.org/packages/b7/8b/7ab8557ff4eb7dd04326631e3caa1029bca8c2d99da5edce047040a4fd00/normal_grain_merge-0.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "17e886b33745f4302b3f81daf8a5220dc0422658c3471f3e1f24f722664f0619",
                "md5": "a2f4e8abf1a2518c89be3e7f497bed5e",
                "sha256": "a6e73af34e9b274975ce24033cca5788f40f44e8f5174b9da045305b89942b60"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp313-cp313-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "a2f4e8abf1a2518c89be3e7f497bed5e",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8",
            "size": 72168,
            "upload_time": "2025-09-07T03:05:17",
            "upload_time_iso_8601": "2025-09-07T03:05:17.365823Z",
            "url": "https://files.pythonhosted.org/packages/17/e8/86b33745f4302b3f81daf8a5220dc0422658c3471f3e1f24f722664f0619/normal_grain_merge-0.0.1-cp313-cp313-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2cb2ace18f703e732f40e548e6ba1f31aba7a492e459c33c871b0003b488fe23",
                "md5": "22d3aa79bb6be1198146e5975f5db285",
                "sha256": "face89f416acbfecafefe06209565d5f08cbe55497ce45b8d5130fb165500947"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp313-cp313-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "22d3aa79bb6be1198146e5975f5db285",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8",
            "size": 73846,
            "upload_time": "2025-09-07T03:05:18",
            "upload_time_iso_8601": "2025-09-07T03:05:18.260625Z",
            "url": "https://files.pythonhosted.org/packages/2c/b2/ace18f703e732f40e548e6ba1f31aba7a492e459c33c871b0003b488fe23/normal_grain_merge-0.0.1-cp313-cp313-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "13f9b81b5e19065a2c3ace17fbd1f82ba7314785ec22a8cbb5e1c5536954dd96",
                "md5": "42cf54c72931d8f6919da8947653690e",
                "sha256": "cf0338a57bd482371ff7847215c851db268c3a13f34c70675bb8cc5aed988b93"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp313-cp313-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "42cf54c72931d8f6919da8947653690e",
            "packagetype": "bdist_wheel",
            "python_version": "cp313",
            "requires_python": ">=3.8",
            "size": 22874,
            "upload_time": "2025-09-07T03:05:19",
            "upload_time_iso_8601": "2025-09-07T03:05:19.269291Z",
            "url": "https://files.pythonhosted.org/packages/13/f9/b81b5e19065a2c3ace17fbd1f82ba7314785ec22a8cbb5e1c5536954dd96/normal_grain_merge-0.0.1-cp313-cp313-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "02a1d7826590a0341d5010e86c8e7b8fe6d2328610ca92e8174d573a50409b11",
                "md5": "23c92b76583f869fdffc7117f5e0a684",
                "sha256": "086b495504bcf3a77009064e1cfd6463b8d5e24cc5f5b829b79da3fc54d0081b"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "23c92b76583f869fdffc7117f5e0a684",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 73835,
            "upload_time": "2025-09-07T03:05:20",
            "upload_time_iso_8601": "2025-09-07T03:05:20.260721Z",
            "url": "https://files.pythonhosted.org/packages/02/a1/d7826590a0341d5010e86c8e7b8fe6d2328610ca92e8174d573a50409b11/normal_grain_merge-0.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "bb1ec9855459d27aea814f27eee650258f9bf4f0e80626b66da618cb3d3fa005",
                "md5": "fd89427cb3b39961fd161ed07af7b186",
                "sha256": "48406fd542b699ffb553348e2011e9503aa13fdbe57178276f004fe1064f3ad3"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "fd89427cb3b39961fd161ed07af7b186",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 71814,
            "upload_time": "2025-09-07T03:05:21",
            "upload_time_iso_8601": "2025-09-07T03:05:21.374638Z",
            "url": "https://files.pythonhosted.org/packages/bb/1e/c9855459d27aea814f27eee650258f9bf4f0e80626b66da618cb3d3fa005/normal_grain_merge-0.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "356d55925cacff8b6763f49483e511f18b9a8a37ab5f48ec0959a9f2b39a519a",
                "md5": "ce35eead4b295786f493df2d9d483a5d",
                "sha256": "f95b24b92563d9711def4d27b80ad56add8c54bf7803dc9de2e8b2a4dc6452be"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp38-cp38-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "ce35eead4b295786f493df2d9d483a5d",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 72926,
            "upload_time": "2025-09-07T03:05:22",
            "upload_time_iso_8601": "2025-09-07T03:05:22.084115Z",
            "url": "https://files.pythonhosted.org/packages/35/6d/55925cacff8b6763f49483e511f18b9a8a37ab5f48ec0959a9f2b39a519a/normal_grain_merge-0.0.1-cp38-cp38-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "e0b6c640141fa99c9e5edcb28cb20d4269f12e01eb93f45b702de3380430032b",
                "md5": "cea7ee5873bb5a07b85f3b3aed86bead",
                "sha256": "420f046240fe4a305abc4a50fe07cd7849beb59d98120282fb2924a11ec76ff5"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp38-cp38-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "cea7ee5873bb5a07b85f3b3aed86bead",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 72857,
            "upload_time": "2025-09-07T03:05:23",
            "upload_time_iso_8601": "2025-09-07T03:05:23.532469Z",
            "url": "https://files.pythonhosted.org/packages/e0/b6/c640141fa99c9e5edcb28cb20d4269f12e01eb93f45b702de3380430032b/normal_grain_merge-0.0.1-cp38-cp38-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c3be977c0eb214482ba46b12b5b249e9e15c1a775db8e8015bfa4de8506a818d",
                "md5": "602d8486951089dc14babb6bd41fb0a8",
                "sha256": "44fe3ecd6f97f3aadf52baae2f16aea4d1385f22fd88886a70c12d4f2c395620"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp38-cp38-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "602d8486951089dc14babb6bd41fb0a8",
            "packagetype": "bdist_wheel",
            "python_version": "cp38",
            "requires_python": ">=3.8",
            "size": 22700,
            "upload_time": "2025-09-07T03:05:24",
            "upload_time_iso_8601": "2025-09-07T03:05:24.276738Z",
            "url": "https://files.pythonhosted.org/packages/c3/be/977c0eb214482ba46b12b5b249e9e15c1a775db8e8015bfa4de8506a818d/normal_grain_merge-0.0.1-cp38-cp38-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "b5e71b9ff2f96d2c1fb0b424bda266d8995f2816760ae5c209f5e781d1d6ae30",
                "md5": "df4c1d42a26fbf8da3f194f35937fbe9",
                "sha256": "9cd73cd74b90111c5aba3ab9354353925b1934dfa65359f87820acc9b324b53e"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "df4c1d42a26fbf8da3f194f35937fbe9",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 71808,
            "upload_time": "2025-09-07T03:05:25",
            "upload_time_iso_8601": "2025-09-07T03:05:25.281131Z",
            "url": "https://files.pythonhosted.org/packages/b5/e7/1b9ff2f96d2c1fb0b424bda266d8995f2816760ae5c209f5e781d1d6ae30/normal_grain_merge-0.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "904a151c8ceab1c83f05cb797178f2fdad3f0ba50b115be5e06cbd24247d0d04",
                "md5": "f2b3d26a3dc00356ef1f2f615cd0075c",
                "sha256": "3b020051e1bdb37081e5c0d2fb7aebfef738ad5573f9737e01755a88b3fde51f"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "has_sig": false,
            "md5_digest": "f2b3d26a3dc00356ef1f2f615cd0075c",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 69850,
            "upload_time": "2025-09-07T03:05:26",
            "upload_time_iso_8601": "2025-09-07T03:05:26.059348Z",
            "url": "https://files.pythonhosted.org/packages/90/4a/151c8ceab1c83f05cb797178f2fdad3f0ba50b115be5e06cbd24247d0d04/normal_grain_merge-0.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "1aa7cd69ebbfbbf23e91836dc88131ef31b392647478d4c920719f5cba6e3ea5",
                "md5": "138c97e69074153fd41edb69deb1c168",
                "sha256": "66e7ae0a3354a2dfc16c037f59d026f310e1ba62c480c50ee35542a6538295ca"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp39-cp39-musllinux_1_2_i686.whl",
            "has_sig": false,
            "md5_digest": "138c97e69074153fd41edb69deb1c168",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 71564,
            "upload_time": "2025-09-07T03:05:27",
            "upload_time_iso_8601": "2025-09-07T03:05:27.093785Z",
            "url": "https://files.pythonhosted.org/packages/1a/a7/cd69ebbfbbf23e91836dc88131ef31b392647478d4c920719f5cba6e3ea5/normal_grain_merge-0.0.1-cp39-cp39-musllinux_1_2_i686.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a6c4f0cd60aab5dd5f4df73c50a9ee4efd359e76621cfc5f63a565a8d2e1df89",
                "md5": "c83225d464c22f2507fb9eec83396033",
                "sha256": "70b34dd6993354630f0f6733121e49497969b6d926ff6a99298a87dbb17d6cea"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp39-cp39-musllinux_1_2_x86_64.whl",
            "has_sig": false,
            "md5_digest": "c83225d464c22f2507fb9eec83396033",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 71453,
            "upload_time": "2025-09-07T03:05:28",
            "upload_time_iso_8601": "2025-09-07T03:05:28.122676Z",
            "url": "https://files.pythonhosted.org/packages/a6/c4/f0cd60aab5dd5f4df73c50a9ee4efd359e76621cfc5f63a565a8d2e1df89/normal_grain_merge-0.0.1-cp39-cp39-musllinux_1_2_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2809b5b39fa698cb251971075efe26250bbd4a24a1bc438df7ac25b73aea24cc",
                "md5": "ce3149b297c8d97568de5245456fb485",
                "sha256": "f47534b5930d5cbefdde959ed04cbccf31ad6116153ff5cb90ffd1ab83a93f89"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1-cp39-cp39-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "ce3149b297c8d97568de5245456fb485",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.8",
            "size": 22816,
            "upload_time": "2025-09-07T03:05:29",
            "upload_time_iso_8601": "2025-09-07T03:05:29.124582Z",
            "url": "https://files.pythonhosted.org/packages/28/09/b5b39fa698cb251971075efe26250bbd4a24a1bc438df7ac25b73aea24cc/normal_grain_merge-0.0.1-cp39-cp39-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "037d08aaf3a067d5757090b6d9cef0ae8f1c73b885a9768aadf2ae659468f2a9",
                "md5": "021be576ee409a5ebc7d8bcf919c667f",
                "sha256": "e0758de95a6ba197d9edaa8d6b77dc76df1b800b6cc4ef5795a225411b8baa16"
            },
            "downloads": -1,
            "filename": "normal_grain_merge-0.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "021be576ee409a5ebc7d8bcf919c667f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 14335,
            "upload_time": "2025-09-07T03:05:30",
            "upload_time_iso_8601": "2025-09-07T03:05:30.314488Z",
            "url": "https://files.pythonhosted.org/packages/03/7d/08aaf3a067d5757090b6d9cef0ae8f1c73b885a9768aadf2ae659468f2a9/normal_grain_merge-0.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-09-07 03:05:30",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "samhaswon",
    "github_project": "normal_grain_merge",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "numpy",
            "specs": [
                [
                    "<",
                    "2.3.0"
                ]
            ]
        },
        {
            "name": "opencv-python",
            "specs": [
                [
                    "==",
                    "4.12.0.88"
                ]
            ]
        }
    ],
    "lcname": "normal-grain-merge"
}
        
Elapsed time: 1.35894s