Skip to content

Commit 4e514b3

Browse files
authored
Improve startup-times by lazy-loading imports (#88)
* Local imports of CV2, requests, and sklearn * fmt * Fix array type * Update changelog
1 parent 84182fe commit 4e514b3

8 files changed

Lines changed: 42 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212

1313
- Raise a ValueError if there are no pixels left after alpha channel masking.
1414

15+
## Fixed
16+
17+
- Local imports improve startup speed for CLI based usage, especially when not
18+
requiring K Means extraction.
19+
1520
# Released
1621

1722
## 4.3.0 - 11/08/2025

Pylette/src/color.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from typing import Literal, cast
33

44
import numpy as np
5-
from PIL import Image
65

76
# Weights for calculating luminance
87
luminance_weights = np.array([0.2126, 0.7152, 0.0722])
@@ -33,6 +32,9 @@ def display(self, w: int = 50, h: int = 50) -> None:
3332
w (int): Width of the window in pixels.
3433
h (int): Height of the window in pixels.
3534
"""
35+
36+
from PIL import Image
37+
3638
img = Image.new("RGBA", size=(w, h), color=self.rgba)
3739
img.show()
3840

Pylette/src/color_extraction.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from typing import Literal
55

66
import numpy as np
7-
import requests
87
from PIL import Image
98

109
from Pylette.src.extractors.k_means import k_means_extraction
@@ -117,6 +116,9 @@ def request_image(image_url: str) -> Image.Image:
117116
Raises:
118117
ValueError: If the URL does not point to a valid image.
119118
"""
119+
120+
import requests
121+
120122
response = requests.get(image_url)
121123
# Check if the request was successful and content type is an image
122124
if response.status_code == 200 and "image" in response.headers.get("Content-Type", ""):

Pylette/src/extractors/k_means.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import numpy as np
22
from numpy.typing import NDArray
3-
from sklearn.cluster import KMeans
43

54
from Pylette.src.color import Color
65
from Pylette.src.extractors.protocol import NP_T, ColorExtractorBase
@@ -20,6 +19,9 @@ def extract(self, arr: NDArray[NP_T], height: int, width: int, palette_size: int
2019
Returns:
2120
list[Color]: A palette of colors sorted by frequency.
2221
"""
22+
23+
from sklearn.cluster import KMeans
24+
2325
arr = np.squeeze(arr)
2426
model = KMeans(n_clusters=palette_size, n_init="auto", init="k-means++", random_state=2024)
2527
labels = model.fit_predict(arr)

Pylette/src/extractors/median_cut.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def extract(self, arr: NDArray[NP_T], height: int, width: int, palette_size: int
3030
return [Color(tuple(map(int, box.average)), box.pixel_count / valid_pixel_count) for box in boxes]
3131

3232

33-
def median_cut_extraction(arr: np.ndarray, height: int, width: int, palette_size: int) -> list[Color]:
33+
def median_cut_extraction(arr: NDArray[NP_T], height: int, width: int, palette_size: int) -> list[Color]:
3434
"""
3535
Extracts a color palette using the median cut algorithm.
3636

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,8 @@ pythonVersion = "3.10"
8282
[build-system]
8383
requires = ["hatchling"]
8484
build-backend = "hatchling.build"
85+
86+
[dependency-groups]
87+
dev = [
88+
"importtime-waterfall>=1.0.0",
89+
]

tests/integration/test_colorspaces.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import pathlib
22
from typing import Literal
33

4-
import cv2
54
import pytest
65
from numpy.testing import assert_approx_equal
7-
from PIL import Image
86

97
from Pylette.src.color_extraction import extract_colors
108

119

1210
@pytest.fixture
1311
def test_image_from_opencv():
12+
import cv2
13+
1414
test_image = pathlib.Path(__file__).parent.parent / "data/test_image.png"
1515
return cv2.imread(str(test_image.absolute().resolve()))
1616

1717

1818
@pytest.fixture
1919
def test_image_from_PIL():
20+
from PIL import Image
21+
2022
test_image = pathlib.Path(__file__).parent.parent / "data/test_image.png"
2123
return Image.open(test_image)
2224

uv.lock

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)