chromatic-python 0.7.4__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/PKG-INFO +1 -1
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/_typing.py +3 -2
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/_version.py +3 -3
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/palette.py +1 -1
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/image/__init__.pyi +1 -6
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/image/_array.py +305 -200
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/PKG-INFO +1 -1
- chromatic_python-0.8.1/chromatic_python.egg-info/scm_version.json +8 -0
- chromatic_python-0.7.4/chromatic_python.egg-info/scm_version.json +0 -8
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/.gitattributes +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/.github/dependabot.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/.github/workflows/docs.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/.github/workflows/release.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/.gitignore +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/README.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/banner.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/__main__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/colorconv.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/colorconv.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/core.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/core.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/color/palette.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/_fetchers.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/fonts/userfont.schema.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/fonts/vga437.ttf +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/images/butterfly.jpg +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/images/escher.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/images/goblin_virus.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/images/registry.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/userfont.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/data/userfont.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/demo.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/image/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/image/_curses.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic/image/_glyph.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/SOURCES.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/dependency_links.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/entry_points.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/requires.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/scm_file_list.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/top_level.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/docs/api/chromatic.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/docs/api/color.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/docs/api/data.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/docs/api/image.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/docs/index.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/mkdocs.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/pyproject.toml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/setup.cfg +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/tests/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.1}/tests/test_color_str.py +0 -0
|
@@ -33,10 +33,11 @@ GlyphBitmask: tp.TypeAlias = GlyphArray[np.bool_]
|
|
|
33
33
|
Bitmask: tp.TypeAlias = MatrixLike[np.bool_]
|
|
34
34
|
GreyscaleGlyphArray: tp.TypeAlias = GlyphArray[np.float64]
|
|
35
35
|
GreyscaleArray: tp.TypeAlias = MatrixLike[np.float64]
|
|
36
|
-
RGBArray: tp.TypeAlias = ShapedNDArray[tuple[int, int, L[3]], np.uint8]
|
|
37
36
|
RGBPixel: tp.TypeAlias = ShapedNDArray[tuple[L[3]], np.uint8]
|
|
37
|
+
RGBArray: tp.TypeAlias = ShapedNDArray[tuple[int, int, L[3]], np.uint8]
|
|
38
|
+
RGBArray3d: tp.TypeAlias = ShapedNDArray[tuple[int, int, int, L[3]], np.uint8]
|
|
38
39
|
|
|
39
|
-
RGBImageLike: tp.TypeAlias = Image | RGBArray
|
|
40
|
+
RGBImageLike: tp.TypeAlias = Image | RGBArray | RGBArray3d
|
|
40
41
|
RGBVectorLike: tp.TypeAlias = IntSequence | RGBPixel
|
|
41
42
|
ColorDictKeys = L["fg", "bg"]
|
|
42
43
|
Ansi4BitAlias = L["4b", 1]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.8.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 1)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'ga30deae42'
|
|
@@ -519,7 +519,7 @@ def rgb_dispatch(*names, replace_defaults=True):
|
|
|
519
519
|
... def func(fruit_or_color="orange", /):
|
|
520
520
|
... res = "fruit" if isinstance(fruit_or_color, str) else "color"
|
|
521
521
|
... return f"{fruit_or_color} is a {res}"
|
|
522
|
-
...
|
|
522
|
+
...
|
|
523
523
|
>>> func()
|
|
524
524
|
'orange is a fruit'
|
|
525
525
|
>>> func("orange")
|
|
@@ -45,10 +45,5 @@ from ._array import (
|
|
|
45
45
|
scale_saturation,
|
|
46
46
|
shuffle_char_set,
|
|
47
47
|
)
|
|
48
|
-
from ._curses import
|
|
49
|
-
ascii_printable,
|
|
50
|
-
backtrans_cp437,
|
|
51
|
-
cp437_printable,
|
|
52
|
-
translate_cp437,
|
|
53
|
-
)
|
|
48
|
+
from ._curses import ascii_printable, backtrans_cp437, cp437_printable, translate_cp437
|
|
54
49
|
from ._glyph import get_glyph_masks, sort_glyphs, ttf_extract_codepoints
|
|
@@ -32,9 +32,7 @@ from shutil import get_terminal_size
|
|
|
32
32
|
|
|
33
33
|
import cv2 as cv
|
|
34
34
|
import numpy as np
|
|
35
|
-
import
|
|
36
|
-
import PIL.ImageDraw
|
|
37
|
-
import PIL.ImageFont
|
|
35
|
+
from PIL import Image, ImageDraw, ImageFont, ImageSequence
|
|
38
36
|
|
|
39
37
|
from .. import _typing as _tp
|
|
40
38
|
from ..color import core
|
|
@@ -43,7 +41,7 @@ from ..color.palette import rgb_dispatch
|
|
|
43
41
|
from ..data import userfont as uf
|
|
44
42
|
|
|
45
43
|
|
|
46
|
-
def get_font_key(font:
|
|
44
|
+
def get_font_key(font: ImageFont.FreeTypeFont):
|
|
47
45
|
"""Obtain a unique tuple pair from a FreeTypeFont object.
|
|
48
46
|
|
|
49
47
|
Parameters
|
|
@@ -80,7 +78,7 @@ def get_font_key(font: PIL.ImageFont.FreeTypeFont):
|
|
|
80
78
|
@tp.overload
|
|
81
79
|
def get_font_object(
|
|
82
80
|
font: _tp.FontArgType, *, retpath: tp.Literal[False] = False
|
|
83
|
-
) ->
|
|
81
|
+
) -> ImageFont.FreeTypeFont: ...
|
|
84
82
|
|
|
85
83
|
|
|
86
84
|
@tp.overload
|
|
@@ -90,13 +88,13 @@ def get_font_object(font: _tp.FontArgType, *, retpath: tp.Literal[True]) -> str:
|
|
|
90
88
|
@tp.overload
|
|
91
89
|
def get_font_object(
|
|
92
90
|
font: _tp.FontArgType, *, retpath: bool
|
|
93
|
-
) ->
|
|
91
|
+
) -> ImageFont.FreeTypeFont | str: ...
|
|
94
92
|
|
|
95
93
|
|
|
96
94
|
@lru_cache
|
|
97
95
|
def get_font_object(
|
|
98
96
|
font: _tp.FontArgType, *, retpath: bool = False
|
|
99
|
-
) ->
|
|
97
|
+
) -> ImageFont.FreeTypeFont | str:
|
|
100
98
|
"""Return a FreeTypeFont object or its filepath.
|
|
101
99
|
|
|
102
100
|
The result is cached to prevent FreeType from consuming excessive resources.
|
|
@@ -123,21 +121,21 @@ def get_font_object(
|
|
|
123
121
|
if retpath:
|
|
124
122
|
return (
|
|
125
123
|
getattr(font.path, "name", os.fspath(font.path))
|
|
126
|
-
if isinstance(font,
|
|
124
|
+
if isinstance(font, ImageFont.FreeTypeFont)
|
|
127
125
|
else get_font_object(get_font_object(font), retpath=True)
|
|
128
126
|
)
|
|
129
127
|
else:
|
|
130
128
|
match font:
|
|
131
|
-
case
|
|
129
|
+
case ImageFont.FreeTypeFont():
|
|
132
130
|
return font
|
|
133
131
|
case uf.UserFont():
|
|
134
132
|
return font.to_truetype()
|
|
135
133
|
case str() if font in uf.userfonts:
|
|
136
134
|
return get_font_object(uf.userfonts[font])
|
|
137
135
|
case str() | os.PathLike():
|
|
138
|
-
return
|
|
136
|
+
return ImageFont.truetype(font, 24)
|
|
139
137
|
raise TypeError(
|
|
140
|
-
f"Expected {
|
|
138
|
+
f"Expected {ImageFont.FreeTypeFont.__name__!r} or pathlike object, "
|
|
141
139
|
f"got {type(font).__name__!r} object instead"
|
|
142
140
|
)
|
|
143
141
|
|
|
@@ -201,7 +199,7 @@ def render_font_str(s: str, /, font: _tp.FontArgType):
|
|
|
201
199
|
for line in map(lambda x: f"{x:<{maxlen}}", lines)
|
|
202
200
|
]
|
|
203
201
|
)
|
|
204
|
-
return
|
|
202
|
+
return Image.fromarray(stacked)
|
|
205
203
|
return render_font_char(s, font)
|
|
206
204
|
|
|
207
205
|
|
|
@@ -239,8 +237,8 @@ def render_font_char(
|
|
|
239
237
|
"""
|
|
240
238
|
if len(c) > 1:
|
|
241
239
|
raise ValueError(f"expected a character, but string of length {len(c)} found")
|
|
242
|
-
img =
|
|
243
|
-
draw =
|
|
240
|
+
img = Image.new("RGB", size=size)
|
|
241
|
+
draw = ImageDraw.Draw(img)
|
|
244
242
|
font_obj = get_font_object(font)
|
|
245
243
|
bbox = draw.textbbox((0, 0), c, font=font_obj)
|
|
246
244
|
x_offset, y_offset = (
|
|
@@ -250,50 +248,6 @@ def render_font_char(
|
|
|
250
248
|
return img
|
|
251
249
|
|
|
252
250
|
|
|
253
|
-
def get_rgb_array(img: str | os.PathLike[str] | _tp.RGBImageLike, /):
|
|
254
|
-
"""Convert an input image into an RGB array.
|
|
255
|
-
|
|
256
|
-
Parameters
|
|
257
|
-
----------
|
|
258
|
-
img : str | PathLike[str] | RGBImageLike
|
|
259
|
-
Input image or path to the image.
|
|
260
|
-
|
|
261
|
-
Returns
|
|
262
|
-
-------
|
|
263
|
-
RGBArray
|
|
264
|
-
|
|
265
|
-
Raises
|
|
266
|
-
------
|
|
267
|
-
ValueError
|
|
268
|
-
If the image format is invalid.
|
|
269
|
-
|
|
270
|
-
TypeError
|
|
271
|
-
If the input is not a valid image or path.
|
|
272
|
-
"""
|
|
273
|
-
if isinstance(img, (str, os.PathLike)):
|
|
274
|
-
x = cv.imread(fp := os.fspath(img))
|
|
275
|
-
if x is None:
|
|
276
|
-
raise FileNotFoundError(fp)
|
|
277
|
-
img = cv.cvtColor(x, cv.COLOR_BGR2RGB)
|
|
278
|
-
if not _is_rgb_array(img):
|
|
279
|
-
if _is_image(img):
|
|
280
|
-
img = img.convert("RGB")
|
|
281
|
-
elif _is_array(img):
|
|
282
|
-
if img.ndim == 2:
|
|
283
|
-
img = cv.cvtColor(img[:, :, 0], cv.COLOR_GRAY2RGB)
|
|
284
|
-
elif img.ndim == 4:
|
|
285
|
-
img = cv.cvtColor(img, cv.COLOR_RGBA2RGB)
|
|
286
|
-
else:
|
|
287
|
-
raise ValueError(f"unexpected array shape: {img.shape!r}")
|
|
288
|
-
else:
|
|
289
|
-
err = TypeError(
|
|
290
|
-
_tp.type_error_msg(img, os.PathLike, PIL.Image.Image, np.ndarray)
|
|
291
|
-
)
|
|
292
|
-
raise err
|
|
293
|
-
img = np.asarray(img, dtype=np.uint8)
|
|
294
|
-
return img
|
|
295
|
-
|
|
296
|
-
|
|
297
251
|
def ansi_quantize(img: _tp.RGBArray, ansi_type: core.AnsiColorParam):
|
|
298
252
|
"""Color-quantize an RGB array into ANSI 4-bit or 8-bit color space.
|
|
299
253
|
|
|
@@ -397,14 +351,151 @@ def scale_saturation(
|
|
|
397
351
|
return img
|
|
398
352
|
|
|
399
353
|
|
|
400
|
-
def
|
|
401
|
-
|
|
402
|
-
):
|
|
403
|
-
return get_rgb_array(img), get_font_object(font)
|
|
354
|
+
def _get_bbox_shape(font: ImageFont.FreeTypeFont, /):
|
|
355
|
+
return font.getbbox(" ")[2:]
|
|
404
356
|
|
|
405
357
|
|
|
406
|
-
|
|
407
|
-
|
|
358
|
+
class _ConversionHandler:
|
|
359
|
+
def _close_owned(self):
|
|
360
|
+
if "owned" in self._ns:
|
|
361
|
+
self._ns.pop("owned").close()
|
|
362
|
+
|
|
363
|
+
def _swap_owned(self, im: Image.Image, /):
|
|
364
|
+
self._close_owned()
|
|
365
|
+
self._ns["owned"] = im
|
|
366
|
+
|
|
367
|
+
def _chars(self):
|
|
368
|
+
char_set = self.char_set
|
|
369
|
+
sort = self.sort_glyphs
|
|
370
|
+
if char_set is None:
|
|
371
|
+
if self.font.path is uf.VGA437:
|
|
372
|
+
from ._curses import cp437_printable as printable
|
|
373
|
+
else:
|
|
374
|
+
from ._curses import ascii_printable as printable
|
|
375
|
+
char_set = printable()
|
|
376
|
+
if not sort:
|
|
377
|
+
char_set = shuffle_char_set(char_set)
|
|
378
|
+
if sort:
|
|
379
|
+
from ._glyph import sort_glyphs
|
|
380
|
+
|
|
381
|
+
char_set = sort_glyphs(char_set, self.font, reverse=sort == -1)
|
|
382
|
+
chars = np.frombuffer(char_set.encode("utf-32-le"), dtype="<U1")
|
|
383
|
+
if not chars.size:
|
|
384
|
+
raise ValueError("empty charset")
|
|
385
|
+
return chars
|
|
386
|
+
|
|
387
|
+
def _push_image(self, im, /):
|
|
388
|
+
if isinstance(im, (str, os.PathLike)):
|
|
389
|
+
im = Image.open(im)
|
|
390
|
+
self._swap_owned(im)
|
|
391
|
+
if _is_image(im):
|
|
392
|
+
if getattr(im, "is_animated", False):
|
|
393
|
+
arr = np.stack(
|
|
394
|
+
ImageSequence.all_frames(
|
|
395
|
+
im, lambda x: x.convert("RGB") # type: ignore
|
|
396
|
+
),
|
|
397
|
+
dtype=np.uint8,
|
|
398
|
+
)
|
|
399
|
+
else:
|
|
400
|
+
arr = np.asarray(im.convert("RGB"), dtype=np.uint8)
|
|
401
|
+
elif _is_array(im):
|
|
402
|
+
arr = im.astype(np.uint8)
|
|
403
|
+
else:
|
|
404
|
+
raise TypeError
|
|
405
|
+
if not arr.size:
|
|
406
|
+
raise ValueError
|
|
407
|
+
if arr.ndim == 2:
|
|
408
|
+
arr = cv.cvtColor(arr[:, :, 0], cv.COLOR_GRAY2RGB)
|
|
409
|
+
elif arr.shape[-1] == 4:
|
|
410
|
+
arr = cv.cvtColor(arr, cv.COLOR_RGBA2RGB)
|
|
411
|
+
if arr.ndim not in {3, 4}:
|
|
412
|
+
raise ValueError
|
|
413
|
+
self._ns["rgb"] = arr
|
|
414
|
+
return arr
|
|
415
|
+
|
|
416
|
+
def __init__(
|
|
417
|
+
self,
|
|
418
|
+
font: _tp.FontArgType = uf.VGA437,
|
|
419
|
+
*,
|
|
420
|
+
factor=200,
|
|
421
|
+
char_set=None,
|
|
422
|
+
sort_glyphs=True,
|
|
423
|
+
ansi_type=None,
|
|
424
|
+
equalize=False,
|
|
425
|
+
bg=None,
|
|
426
|
+
):
|
|
427
|
+
self.font = get_font_object(font)
|
|
428
|
+
self.factor = factor
|
|
429
|
+
self.char_set = char_set
|
|
430
|
+
self.sort_glyphs = sort_glyphs
|
|
431
|
+
self.ansi_type = core.get_ansi_type(ansi_type)
|
|
432
|
+
self.equalize = equalize
|
|
433
|
+
self.bg = bg
|
|
434
|
+
self._ns = {}
|
|
435
|
+
|
|
436
|
+
def __enter__(self):
|
|
437
|
+
return self
|
|
438
|
+
|
|
439
|
+
def __exit__(self, *exc_info):
|
|
440
|
+
self._close_owned()
|
|
441
|
+
self._ns.clear()
|
|
442
|
+
|
|
443
|
+
def to_ascii(self, im, /):
|
|
444
|
+
rgb = self._push_image(im)
|
|
445
|
+
if is_single := rgb.ndim == 3:
|
|
446
|
+
rgb = rgb[None, :]
|
|
447
|
+
chars = self._chars()
|
|
448
|
+
ch, cw = _get_bbox_shape(self.font)
|
|
449
|
+
out_shape = (
|
|
450
|
+
int(self.factor / (rgb.shape[2] / rgb.shape[1]) / ceil(cw / ch)),
|
|
451
|
+
self.factor,
|
|
452
|
+
)
|
|
453
|
+
interp = np.empty((rgb.shape[0], *out_shape), dtype="<U1")
|
|
454
|
+
for i in range(rgb.shape[0]):
|
|
455
|
+
grey = cv.cvtColor(rgb[i], cv.COLOR_RGB2GRAY)
|
|
456
|
+
blur = grey.astype(np.float64) / 255.0
|
|
457
|
+
for t, (ax_in, ax_out) in enumerate(zip(grey.shape, out_shape)):
|
|
458
|
+
if (s := (ax_in / ax_out - 1) / 2) <= 0:
|
|
459
|
+
continue
|
|
460
|
+
k = cv.getGaussianKernel(2 * int(4.0 * s + 0.5) + 1, s)
|
|
461
|
+
if t:
|
|
462
|
+
k = k.T
|
|
463
|
+
blur = cv.filter2D(blur, -1, k, borderType=cv.BORDER_REFLECT_101)
|
|
464
|
+
grey = (
|
|
465
|
+
cv.resize(blur, out_shape[::-1], interpolation=cv.INTER_LINEAR) * 255.0
|
|
466
|
+
).astype(np.uint8)
|
|
467
|
+
interp[i] = chars[np.rint(grey / 255 * (chars.size - 1)).astype(np.intp)]
|
|
468
|
+
return interp[0] if is_single else interp
|
|
469
|
+
|
|
470
|
+
def to_ansi(self, im, /):
|
|
471
|
+
interp = self.to_ascii(im)
|
|
472
|
+
rgb = self._ns["rgb"]
|
|
473
|
+
assert interp.ndim == rgb.ndim - 1
|
|
474
|
+
if is_single := rgb.ndim == 3:
|
|
475
|
+
interp = interp[None, :]
|
|
476
|
+
rgb = rgb[None, :]
|
|
477
|
+
assert interp.shape[0] == rgb.shape[0]
|
|
478
|
+
if self.equalize is True:
|
|
479
|
+
rgb[:] = [contrast_stretch(x) for x in rgb]
|
|
480
|
+
elif self.equalize == "white_point":
|
|
481
|
+
rgb[:] = [equalize_white_point(x) for x in rgb]
|
|
482
|
+
rgb[:] = ansi_quantize(rgb, ansi_type=self.ansi_type)
|
|
483
|
+
newshape = interp.shape[-2:]
|
|
484
|
+
n_frames = interp.shape[0]
|
|
485
|
+
out = np.empty((n_frames, *newshape), dtype=core.color_chain.dtype)
|
|
486
|
+
for i in range(n_frames):
|
|
487
|
+
with (
|
|
488
|
+
Image.fromarray(rgb[i], mode="RGB") as x,
|
|
489
|
+
x.resize(newshape[::-1], resample=Image.Resampling.LANCZOS) as xr,
|
|
490
|
+
):
|
|
491
|
+
out["char"][i] = interp[i]
|
|
492
|
+
out["rgb"][i, ..., 0, 0] = self.ansi_type.typecode
|
|
493
|
+
out["rgb"][i, ..., 0, 1:] = np.asarray(xr, dtype=np.uint8)
|
|
494
|
+
if self.bg is None:
|
|
495
|
+
continue
|
|
496
|
+
out["rgb"][i, ..., 1, 0] = self.ansi_type.typecode
|
|
497
|
+
out["rgb"][i, ..., 1, 1:] = self.bg
|
|
498
|
+
return out[0] if is_single else out
|
|
408
499
|
|
|
409
500
|
|
|
410
501
|
@tp.overload
|
|
@@ -416,23 +507,49 @@ def img2ascii(
|
|
|
416
507
|
char_set: tp.Optional[str] = ...,
|
|
417
508
|
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
418
509
|
*,
|
|
419
|
-
|
|
420
|
-
) -> str: ...
|
|
510
|
+
outarray: tp.Literal[False] = False,
|
|
511
|
+
) -> str | list[str]: ...
|
|
421
512
|
|
|
422
513
|
|
|
423
514
|
@tp.overload
|
|
424
515
|
def img2ascii(
|
|
425
|
-
img:
|
|
516
|
+
img: _tp.RGBArray,
|
|
517
|
+
/,
|
|
518
|
+
font: _tp.FontArgType = ...,
|
|
519
|
+
factor: int = ...,
|
|
520
|
+
char_set: tp.Optional[str] = ...,
|
|
521
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
522
|
+
*,
|
|
523
|
+
outarray: tp.Literal[True],
|
|
524
|
+
) -> _tp.ShapedNDArray[tuple[int, int], np.str_]: ...
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
@tp.overload
|
|
528
|
+
def img2ascii(
|
|
529
|
+
img: _tp.RGBArray3d,
|
|
530
|
+
/,
|
|
531
|
+
font: _tp.FontArgType = ...,
|
|
532
|
+
factor: int = ...,
|
|
533
|
+
char_set: tp.Optional[str] = ...,
|
|
534
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
535
|
+
*,
|
|
536
|
+
outarray: tp.Literal[True],
|
|
537
|
+
) -> _tp.ShapedNDArray[tuple[int, int, int], np.str_]: ...
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
@tp.overload
|
|
541
|
+
def img2ascii(
|
|
542
|
+
img: str | os.PathLike[str] | Image.Image,
|
|
426
543
|
/,
|
|
427
544
|
font: _tp.FontArgType = ...,
|
|
428
545
|
factor: int = ...,
|
|
429
546
|
char_set: tp.Optional[str] = ...,
|
|
430
547
|
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
431
548
|
*,
|
|
432
|
-
|
|
433
|
-
) ->
|
|
549
|
+
outarray: tp.Literal[True],
|
|
550
|
+
) -> tp.Union[
|
|
434
551
|
_tp.ShapedNDArray[tuple[int, int], np.str_],
|
|
435
|
-
_tp.ShapedNDArray[tuple[int, int,
|
|
552
|
+
_tp.ShapedNDArray[tuple[int, int, int], np.str_],
|
|
436
553
|
]: ...
|
|
437
554
|
|
|
438
555
|
|
|
@@ -444,7 +561,7 @@ def img2ascii( # type: ignore
|
|
|
444
561
|
char_set=None,
|
|
445
562
|
sort_glyphs=True,
|
|
446
563
|
*,
|
|
447
|
-
|
|
564
|
+
outarray=False,
|
|
448
565
|
):
|
|
449
566
|
"""Convert an image to a multiline ASCII string.
|
|
450
567
|
|
|
@@ -462,17 +579,15 @@ def img2ascii( # type: ignore
|
|
|
462
579
|
char_set : Iterable[str], optional
|
|
463
580
|
Characters to be mapped to greyscale values of 'img'.
|
|
464
581
|
|
|
465
|
-
sort_glyphs : {True, False,
|
|
582
|
+
sort_glyphs : {True, False, -1}
|
|
466
583
|
Specifies to sort `char_set` or leave it unsorted before mapping to greyscale.
|
|
467
584
|
|
|
468
585
|
Glyph bitmasks obtained from 'font' are compared when sorting the string.
|
|
469
586
|
|
|
470
|
-
|
|
587
|
+
`-1` specifies reverse sorting order.
|
|
471
588
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
Used by ``img2ansi`` to lazily obtain the base ASCII chars and original RGB array.
|
|
589
|
+
outarray : bool, default=False
|
|
590
|
+
If True, returns the raw chararray. Otherwise, returns a string or list of strings.
|
|
476
591
|
|
|
477
592
|
Returns
|
|
478
593
|
-------
|
|
@@ -488,58 +603,19 @@ def img2ascii( # type: ignore
|
|
|
488
603
|
--------
|
|
489
604
|
ascii2img : Render an ASCII string as an image.
|
|
490
605
|
"""
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
out_h = int(factor / img_aspect / char_aspect)
|
|
499
|
-
out_w = factor
|
|
500
|
-
blur = grey.astype(np.float64) / 255.0
|
|
501
|
-
if (sy := (shape[0] / out_h - 1) / 2) > 0:
|
|
502
|
-
blur = cv.filter2D(
|
|
503
|
-
blur,
|
|
504
|
-
-1,
|
|
505
|
-
cv.getGaussianKernel(2 * int(4.0 * sy + 0.5) + 1, sy),
|
|
506
|
-
borderType=cv.BORDER_REFLECT_101,
|
|
507
|
-
)
|
|
508
|
-
if (sx := (shape[1] / out_w - 1) / 2) > 0:
|
|
509
|
-
blur = cv.filter2D(
|
|
510
|
-
blur,
|
|
511
|
-
-1,
|
|
512
|
-
cv.getGaussianKernel(2 * int(4.0 * sx + 0.5) + 1, sx).T,
|
|
513
|
-
borderType=cv.BORDER_REFLECT_101,
|
|
514
|
-
)
|
|
515
|
-
grey = (
|
|
516
|
-
cv.resize(blur, (out_w, out_h), interpolation=cv.INTER_LINEAR) * 255.0
|
|
517
|
-
).astype(np.uint8)
|
|
518
|
-
if char_set is None:
|
|
519
|
-
if font.path is uf.VGA437:
|
|
520
|
-
from ._curses import cp437_printable
|
|
521
|
-
|
|
522
|
-
char_set = cp437_printable()
|
|
523
|
-
else:
|
|
524
|
-
from ._curses import ascii_printable
|
|
525
|
-
|
|
526
|
-
char_set = ascii_printable()
|
|
527
|
-
if not sort_glyphs:
|
|
528
|
-
char_set = shuffle_char_set(char_set)
|
|
529
|
-
if sort_glyphs:
|
|
530
|
-
from ._glyph import sort_glyphs as glyph_sort
|
|
531
|
-
|
|
532
|
-
char_set = glyph_sort(char_set, font, reverse=not ~int(sort_glyphs))
|
|
533
|
-
chars = np.frombuffer(char_set.encode("utf-32-le"), dtype="<U1")
|
|
534
|
-
if not chars.size:
|
|
535
|
-
raise ValueError("empty charset")
|
|
536
|
-
interp = chars[np.rint(grey / 255 * (chars.size - 1)).astype(np.intp)]
|
|
537
|
-
if ret_img is True:
|
|
538
|
-
return interp, rgb
|
|
539
|
-
newlines = np.zeros((interp.shape[0], 1), dtype="<U1")
|
|
606
|
+
with _ConversionHandler(
|
|
607
|
+
font, factor=factor, char_set=char_set, sort_glyphs=sort_glyphs
|
|
608
|
+
) as h:
|
|
609
|
+
out = h.to_ascii(img)
|
|
610
|
+
if outarray is True:
|
|
611
|
+
return out
|
|
612
|
+
newlines = np.zeros((*out.shape[:-1], 1), dtype="<U1")
|
|
540
613
|
newlines[:-1] = "\n"
|
|
541
|
-
|
|
542
|
-
|
|
614
|
+
out = np.concatenate((out, newlines), axis=-1)
|
|
615
|
+
if out.ndim == 3:
|
|
616
|
+
return "".join(out.flat)
|
|
617
|
+
else:
|
|
618
|
+
return ["".join(x.flat) for x in out]
|
|
543
619
|
|
|
544
620
|
|
|
545
621
|
@tp.overload
|
|
@@ -555,12 +631,12 @@ def img2ansi(
|
|
|
555
631
|
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
556
632
|
*,
|
|
557
633
|
outarray: tp.Literal[False] = False,
|
|
558
|
-
) -> core.color_chain: ...
|
|
634
|
+
) -> core.color_chain | list[core.color_chain]: ...
|
|
559
635
|
|
|
560
636
|
|
|
561
637
|
@tp.overload
|
|
562
638
|
def img2ansi(
|
|
563
|
-
img:
|
|
639
|
+
img: _tp.RGBArray,
|
|
564
640
|
/,
|
|
565
641
|
font: _tp.FontArgType = ...,
|
|
566
642
|
factor: int = ...,
|
|
@@ -574,17 +650,52 @@ def img2ansi(
|
|
|
574
650
|
) -> _tp.ShapedNDArray[tuple[int, int], np.void]: ...
|
|
575
651
|
|
|
576
652
|
|
|
577
|
-
@
|
|
653
|
+
@tp.overload
|
|
578
654
|
def img2ansi(
|
|
579
|
-
img:
|
|
655
|
+
img: _tp.RGBArray3d,
|
|
580
656
|
/,
|
|
581
|
-
font: _tp.FontArgType =
|
|
582
|
-
factor: int =
|
|
583
|
-
char_set: tp.Optional[str] =
|
|
584
|
-
sort_glyphs: bool | tp.Literal[-1] =
|
|
585
|
-
ansi_type: tp.Optional[core.AnsiColorParam] =
|
|
586
|
-
equalize: bool | tp.Literal["white_point"] =
|
|
587
|
-
bg: tp.Optional[_tp.Int3Tuple | str] =
|
|
657
|
+
font: _tp.FontArgType = ...,
|
|
658
|
+
factor: int = ...,
|
|
659
|
+
char_set: tp.Optional[str] = ...,
|
|
660
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
661
|
+
ansi_type: tp.Optional[core.AnsiColorParam] = ...,
|
|
662
|
+
equalize: bool | tp.Literal["white_point"] = ...,
|
|
663
|
+
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
664
|
+
*,
|
|
665
|
+
outarray: tp.Literal[True],
|
|
666
|
+
) -> _tp.ShapedNDArray[tuple[int, int, int], np.void]: ...
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
@tp.overload
|
|
670
|
+
def img2ansi(
|
|
671
|
+
img: str | os.PathLike[str] | Image.Image,
|
|
672
|
+
/,
|
|
673
|
+
font: _tp.FontArgType = ...,
|
|
674
|
+
factor: int = ...,
|
|
675
|
+
char_set: tp.Optional[str] = ...,
|
|
676
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
677
|
+
ansi_type: tp.Optional[core.AnsiColorParam] = ...,
|
|
678
|
+
equalize: bool | tp.Literal["white_point"] = ...,
|
|
679
|
+
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
680
|
+
*,
|
|
681
|
+
outarray: tp.Literal[True],
|
|
682
|
+
) -> tp.Union[
|
|
683
|
+
_tp.ShapedNDArray[tuple[int, int], np.void],
|
|
684
|
+
_tp.ShapedNDArray[tuple[int, int, int], np.void],
|
|
685
|
+
]: ...
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
@rgb_dispatch("bg")
|
|
689
|
+
def img2ansi( # type: ignore
|
|
690
|
+
img,
|
|
691
|
+
/,
|
|
692
|
+
font=uf.VGA437,
|
|
693
|
+
factor=200,
|
|
694
|
+
char_set=None,
|
|
695
|
+
sort_glyphs=True,
|
|
696
|
+
ansi_type=None,
|
|
697
|
+
equalize=False,
|
|
698
|
+
bg=None,
|
|
588
699
|
*,
|
|
589
700
|
outarray=False,
|
|
590
701
|
):
|
|
@@ -607,12 +718,12 @@ def img2ansi(
|
|
|
607
718
|
|
|
608
719
|
If None (default), the character set will be determined based on the 'font' parameter.
|
|
609
720
|
|
|
610
|
-
sort_glyphs : {True, False,
|
|
721
|
+
sort_glyphs : {True, False, -1}
|
|
611
722
|
Specifies to sort `char_set` or leave it unsorted before mapping to greyscale.
|
|
612
723
|
|
|
613
724
|
Glyph bitmasks obtained from 'font' are compared when sorting the string.
|
|
614
725
|
|
|
615
|
-
|
|
726
|
+
`-1` specifies reverse sorting order.
|
|
616
727
|
|
|
617
728
|
ansi_type : AnsiColorParam
|
|
618
729
|
ANSI color format to map the RGB values to.
|
|
@@ -656,30 +767,22 @@ def img2ansi(
|
|
|
656
767
|
ansi2img : Render an ANSI array as an image.
|
|
657
768
|
img2ascii : Used to obtain the base ASCII characters.
|
|
658
769
|
"""
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
out = np.zeros(s.shape, dtype=core.color_chain.dtype)
|
|
676
|
-
out["char"] = s
|
|
677
|
-
out["rgb"][..., 0, 0] = ansi_type.typecode
|
|
678
|
-
out["rgb"][..., 0, 1:] = np.asarray(resized, dtype=np.uint8)
|
|
679
|
-
if bg is not None:
|
|
680
|
-
out["rgb"][..., 1, 0] = ansi_type.typecode
|
|
681
|
-
out["rgb"][..., 1, 1:] = bg
|
|
682
|
-
return out if outarray is True else core.color_chain.fromarray(out)
|
|
770
|
+
with _ConversionHandler(
|
|
771
|
+
font,
|
|
772
|
+
factor=factor,
|
|
773
|
+
char_set=char_set,
|
|
774
|
+
sort_glyphs=sort_glyphs,
|
|
775
|
+
ansi_type=ansi_type,
|
|
776
|
+
equalize=equalize,
|
|
777
|
+
bg=bg,
|
|
778
|
+
) as h:
|
|
779
|
+
out = h.to_ansi(img)
|
|
780
|
+
if outarray is True:
|
|
781
|
+
return out
|
|
782
|
+
elif out.ndim == 2:
|
|
783
|
+
return core.color_chain.fromarray(out)
|
|
784
|
+
else:
|
|
785
|
+
return [core.color_chain.fromarray(x) for x in out]
|
|
683
786
|
|
|
684
787
|
|
|
685
788
|
@rgb_dispatch("fg", "bg")
|
|
@@ -714,20 +817,20 @@ def ascii2img(
|
|
|
714
817
|
Returns
|
|
715
818
|
-------
|
|
716
819
|
ascii_img : Image
|
|
717
|
-
A `
|
|
820
|
+
A `Image.Image` object of the rendered ASCII string.
|
|
718
821
|
|
|
719
822
|
See Also
|
|
720
823
|
--------
|
|
721
824
|
img2ascii : Convert an image into an ASCII string.
|
|
722
825
|
"""
|
|
723
|
-
font =
|
|
826
|
+
font = ImageFont.truetype(get_font_object(font, retpath=True), font_size)
|
|
724
827
|
lines = s.split("\n")
|
|
725
828
|
n_rows, n_cols = map(len, (lines, lines[0]))
|
|
726
829
|
cw, ch = _get_bbox_shape(font)
|
|
727
830
|
iw, ih = (int(i * j) for i, j in zip((cw, ch), (n_cols, n_rows)))
|
|
728
831
|
r, g, b = tuple(map(int, bg))
|
|
729
|
-
img =
|
|
730
|
-
draw =
|
|
832
|
+
img = Image.new("RGB", (iw, ih), (r, g, b))
|
|
833
|
+
draw = ImageDraw.Draw(img)
|
|
731
834
|
y_offset = 0
|
|
732
835
|
for line in lines:
|
|
733
836
|
draw.text((0, y_offset), line, font=font, fill=fg)
|
|
@@ -740,7 +843,6 @@ def ansi2img(
|
|
|
740
843
|
arr: (
|
|
741
844
|
_tp.ShapedNDArray[tuple[int, int], np.void]
|
|
742
845
|
| core.color_chain
|
|
743
|
-
| list[core.color_chain]
|
|
744
846
|
| list[list[core.ColorStr]]
|
|
745
847
|
),
|
|
746
848
|
/,
|
|
@@ -772,7 +874,7 @@ def ansi2img(
|
|
|
772
874
|
Returns
|
|
773
875
|
-------
|
|
774
876
|
ansi_img : Image
|
|
775
|
-
The rendered ANSI array as an `
|
|
877
|
+
The rendered ANSI array as an `Image.Image` object.
|
|
776
878
|
|
|
777
879
|
Raises
|
|
778
880
|
------
|
|
@@ -792,7 +894,7 @@ def ansi2img(
|
|
|
792
894
|
if not arr.size:
|
|
793
895
|
raise ValueError("input array is empty")
|
|
794
896
|
|
|
795
|
-
font =
|
|
897
|
+
font = ImageFont.truetype(get_font_object(font, retpath=True), font_size)
|
|
796
898
|
bbox_h = _get_bbox_shape(font)[-1]
|
|
797
899
|
widths = np.asarray(
|
|
798
900
|
[[font.getbbox(c)[2] for c in x["char"]] for x in arr], dtype=np.uint32
|
|
@@ -813,10 +915,8 @@ def ansi2img(
|
|
|
813
915
|
if rgba:
|
|
814
916
|
mode = "RGBA"
|
|
815
917
|
rgba_descr = arr.dtype.descr.copy()
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
subarr_shape = subarr_shape[:-1] + (subarr_shape[-1] + 1,)
|
|
819
|
-
rgba_descr[-1] = rgb_field[:-1] + (subarr_shape,)
|
|
918
|
+
*rgb_args, (subd1, subd2) = rgba_descr[-1]
|
|
919
|
+
rgba_descr[-1] = (*rgb_args, (subd1, subd2 + 1))
|
|
820
920
|
arr = arr.astype(rgba_descr)
|
|
821
921
|
arr["rgb"][..., 0, -1] = 0xFF
|
|
822
922
|
else:
|
|
@@ -827,8 +927,8 @@ def ansi2img(
|
|
|
827
927
|
arr["rgb"][mask, i, 0] = 1
|
|
828
928
|
arr["rgb"][mask, i, 1 : len(fill) + 1] = fill
|
|
829
929
|
|
|
830
|
-
img =
|
|
831
|
-
draw =
|
|
930
|
+
img = Image.new(mode, (iw, ih), bg_default)
|
|
931
|
+
draw = ImageDraw.Draw(img)
|
|
832
932
|
y_offset = 0
|
|
833
933
|
for y in range(arr.shape[0]):
|
|
834
934
|
x_offset = 0
|
|
@@ -860,36 +960,41 @@ def ansify(
|
|
|
860
960
|
fg: _tp.Int3Tuple | str = (170, 170, 170),
|
|
861
961
|
bg: _tp.Int3Tuple | str = (0, 0, 0),
|
|
862
962
|
):
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
img2ansi(
|
|
866
|
-
img,
|
|
867
|
-
font,
|
|
868
|
-
factor=factor,
|
|
869
|
-
char_set=char_set,
|
|
870
|
-
ansi_type=ansi_type,
|
|
871
|
-
sort_glyphs=sort_glyphs,
|
|
872
|
-
equalize=equalize,
|
|
873
|
-
bg=bg,
|
|
874
|
-
outarray=True,
|
|
875
|
-
),
|
|
963
|
+
arr = img2ansi(
|
|
964
|
+
img,
|
|
876
965
|
font,
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
966
|
+
factor=factor,
|
|
967
|
+
char_set=char_set,
|
|
968
|
+
ansi_type=ansi_type,
|
|
969
|
+
sort_glyphs=sort_glyphs,
|
|
970
|
+
equalize=equalize,
|
|
971
|
+
bg=bg,
|
|
972
|
+
outarray=True,
|
|
880
973
|
)
|
|
974
|
+
if arr.ndim == 4:
|
|
975
|
+
arr = arr[0]
|
|
976
|
+
assert _is_cc_array2d(arr)
|
|
977
|
+
return ansi2img(arr, font, font_size=font_size, fg_default=fg, bg_default=bg)
|
|
881
978
|
|
|
882
979
|
|
|
883
980
|
def _is_array(obj: tp.Any, /) -> tp.TypeGuard[np.ndarray]:
|
|
884
981
|
return isinstance(obj, np.ndarray)
|
|
885
982
|
|
|
886
983
|
|
|
887
|
-
def
|
|
888
|
-
|
|
984
|
+
def _is_cc_array(
|
|
985
|
+
obj: tp.Any, /
|
|
986
|
+
) -> tp.TypeGuard[_tp.ShapedNDArray[tuple[int, ...], np.void]]:
|
|
987
|
+
return _is_array(obj) and np.issubdtype(obj.dtype, core.color_chain.dtype)
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
def _is_cc_array2d(
|
|
991
|
+
obj: tp.Any, /
|
|
992
|
+
) -> tp.TypeGuard[_tp.ShapedNDArray[tuple[int, int], np.void]]:
|
|
993
|
+
return _is_cc_array(obj) and obj.ndim == 2
|
|
889
994
|
|
|
890
995
|
|
|
891
|
-
def _is_image(obj: tp.Any, /) -> tp.TypeGuard[
|
|
892
|
-
return isinstance(obj,
|
|
996
|
+
def _is_image(obj: tp.Any, /) -> tp.TypeGuard[Image.Image]:
|
|
997
|
+
return isinstance(obj, Image.Image)
|
|
893
998
|
|
|
894
999
|
|
|
895
1000
|
@lru_cache(maxsize=1)
|
|
@@ -1235,7 +1340,7 @@ def render_ans(
|
|
|
1235
1340
|
font_size: int = 16,
|
|
1236
1341
|
*,
|
|
1237
1342
|
bg_default: _tp.Int3Tuple | _tp.TupleOf4[int] | str = (0, 0, 0),
|
|
1238
|
-
) ->
|
|
1343
|
+
) -> Image.Image:
|
|
1239
1344
|
"""Return an image render of an ANS file.
|
|
1240
1345
|
|
|
1241
1346
|
Parameters
|
|
@@ -1278,7 +1383,7 @@ def render_ans(
|
|
|
1278
1383
|
|
|
1279
1384
|
|
|
1280
1385
|
def otsu_mask(
|
|
1281
|
-
img: _tp.MatrixLike[np.uint8] | cv.typing.MatLike |
|
|
1386
|
+
img: _tp.MatrixLike[np.uint8] | cv.typing.MatLike | Image.Image,
|
|
1282
1387
|
) -> _tp.MatrixLike[np.uint8]:
|
|
1283
1388
|
img = np.asarray(img, dtype=np.uint8)
|
|
1284
1389
|
kernel = cv.getStructuringElement(cv.MORPH_RECT, (2, 2))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{chromatic_python-0.7.4 → chromatic_python-0.8.1}/chromatic_python.egg-info/scm_file_list.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|