chromatic-python 0.7.4__tar.gz → 0.8.0__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.0}/PKG-INFO +1 -1
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/_typing.py +3 -2
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/_version.py +3 -3
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/palette.py +1 -1
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/image/__init__.pyi +1 -6
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/image/_array.py +312 -200
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/PKG-INFO +1 -1
- chromatic_python-0.8.0/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.0}/.gitattributes +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/.github/dependabot.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/.github/workflows/docs.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/.github/workflows/release.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/.gitignore +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/README.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/banner.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/__main__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/colorconv.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/colorconv.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/core.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/core.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/color/palette.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/__init__.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/_fetchers.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/fonts/userfont.schema.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/fonts/vga437.ttf +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/images/butterfly.jpg +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/images/escher.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/images/goblin_virus.png +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/images/registry.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/userfont.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/data/userfont.pyi +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/demo.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/image/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/image/_curses.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic/image/_glyph.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/SOURCES.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/dependency_links.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/entry_points.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/requires.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/scm_file_list.json +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/top_level.txt +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/docs/api/chromatic.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/docs/api/color.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/docs/api/data.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/docs/api/image.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/docs/index.md +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/mkdocs.yml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/pyproject.toml +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/setup.cfg +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/tests/__init__.py +0 -0
- {chromatic_python-0.7.4 → chromatic_python-0.8.0}/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.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 8, 0)
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'g1f588062b'
|
|
@@ -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,158 @@ 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 = np.asarray(im, dtype=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
|
+
char_aspect = ceil(cw / ch)
|
|
450
|
+
img_aspect = rgb.shape[2] / rgb.shape[1]
|
|
451
|
+
out_w = self.factor
|
|
452
|
+
out_h = int(self.factor / img_aspect / char_aspect)
|
|
453
|
+
interp = np.empty((rgb.shape[0], out_h, out_w), 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
|
+
if (sy := (grey.shape[0] / out_h - 1) / 2) > 0:
|
|
458
|
+
blur = cv.filter2D(
|
|
459
|
+
blur,
|
|
460
|
+
-1,
|
|
461
|
+
cv.getGaussianKernel(2 * int(4.0 * sy + 0.5) + 1, sy),
|
|
462
|
+
borderType=cv.BORDER_REFLECT_101,
|
|
463
|
+
)
|
|
464
|
+
if (sx := (grey.shape[1] / out_w - 1) / 2) > 0:
|
|
465
|
+
blur = cv.filter2D(
|
|
466
|
+
blur,
|
|
467
|
+
-1,
|
|
468
|
+
cv.getGaussianKernel(2 * int(4.0 * sx + 0.5) + 1, sx).T,
|
|
469
|
+
borderType=cv.BORDER_REFLECT_101,
|
|
470
|
+
)
|
|
471
|
+
grey = (
|
|
472
|
+
cv.resize(blur, (out_w, out_h), interpolation=cv.INTER_LINEAR) * 255.0
|
|
473
|
+
).astype(np.uint8)
|
|
474
|
+
interp[i] = chars[np.rint(grey / 255 * (chars.size - 1)).astype(np.intp)]
|
|
475
|
+
return interp[0] if is_single else interp
|
|
476
|
+
|
|
477
|
+
def to_ansi(self, im, /):
|
|
478
|
+
interp = self.to_ascii(im)
|
|
479
|
+
rgb = self._ns["rgb"]
|
|
480
|
+
assert interp.ndim == rgb.ndim - 1
|
|
481
|
+
if is_single := rgb.ndim == 3:
|
|
482
|
+
interp = interp[None, :]
|
|
483
|
+
rgb = rgb[None, :]
|
|
484
|
+
assert interp.shape[0] == rgb.shape[0]
|
|
485
|
+
if self.equalize is True:
|
|
486
|
+
rgb[:] = [contrast_stretch(x) for x in rgb]
|
|
487
|
+
elif self.equalize == "white_point":
|
|
488
|
+
rgb[:] = [equalize_white_point(x) for x in rgb]
|
|
489
|
+
rgb[:] = ansi_quantize(rgb, ansi_type=self.ansi_type)
|
|
490
|
+
newshape = interp.shape[-2:]
|
|
491
|
+
n_frames = interp.shape[0]
|
|
492
|
+
out = np.empty((n_frames, *newshape), dtype=core.color_chain.dtype)
|
|
493
|
+
for i in range(n_frames):
|
|
494
|
+
with (
|
|
495
|
+
Image.fromarray(rgb[i], mode="RGB") as x,
|
|
496
|
+
x.resize(newshape[::-1], resample=Image.Resampling.LANCZOS) as xr,
|
|
497
|
+
):
|
|
498
|
+
out["char"][i] = interp[i]
|
|
499
|
+
out["rgb"][i, ..., 0, 0] = self.ansi_type.typecode
|
|
500
|
+
out["rgb"][i, ..., 0, 1:] = np.asarray(xr, dtype=np.uint8)
|
|
501
|
+
if self.bg is None:
|
|
502
|
+
continue
|
|
503
|
+
out["rgb"][i, ..., 1, 0] = self.ansi_type.typecode
|
|
504
|
+
out["rgb"][i, ..., 1, 1:] = self.bg
|
|
505
|
+
return out[0] if is_single else out
|
|
408
506
|
|
|
409
507
|
|
|
410
508
|
@tp.overload
|
|
@@ -416,23 +514,49 @@ def img2ascii(
|
|
|
416
514
|
char_set: tp.Optional[str] = ...,
|
|
417
515
|
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
418
516
|
*,
|
|
419
|
-
|
|
420
|
-
) -> str: ...
|
|
517
|
+
outarray: tp.Literal[False] = False,
|
|
518
|
+
) -> str | list[str]: ...
|
|
421
519
|
|
|
422
520
|
|
|
423
521
|
@tp.overload
|
|
424
522
|
def img2ascii(
|
|
425
|
-
img:
|
|
523
|
+
img: _tp.RGBArray,
|
|
524
|
+
/,
|
|
525
|
+
font: _tp.FontArgType = ...,
|
|
526
|
+
factor: int = ...,
|
|
527
|
+
char_set: tp.Optional[str] = ...,
|
|
528
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
529
|
+
*,
|
|
530
|
+
outarray: tp.Literal[True],
|
|
531
|
+
) -> _tp.ShapedNDArray[tuple[int, int], np.str_]: ...
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@tp.overload
|
|
535
|
+
def img2ascii(
|
|
536
|
+
img: _tp.RGBArray3d,
|
|
537
|
+
/,
|
|
538
|
+
font: _tp.FontArgType = ...,
|
|
539
|
+
factor: int = ...,
|
|
540
|
+
char_set: tp.Optional[str] = ...,
|
|
541
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
542
|
+
*,
|
|
543
|
+
outarray: tp.Literal[True],
|
|
544
|
+
) -> _tp.ShapedNDArray[tuple[int, int, int], np.str_]: ...
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
@tp.overload
|
|
548
|
+
def img2ascii(
|
|
549
|
+
img: str | os.PathLike[str] | Image.Image,
|
|
426
550
|
/,
|
|
427
551
|
font: _tp.FontArgType = ...,
|
|
428
552
|
factor: int = ...,
|
|
429
553
|
char_set: tp.Optional[str] = ...,
|
|
430
554
|
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
431
555
|
*,
|
|
432
|
-
|
|
433
|
-
) ->
|
|
556
|
+
outarray: tp.Literal[True],
|
|
557
|
+
) -> tp.Union[
|
|
434
558
|
_tp.ShapedNDArray[tuple[int, int], np.str_],
|
|
435
|
-
_tp.ShapedNDArray[tuple[int, int,
|
|
559
|
+
_tp.ShapedNDArray[tuple[int, int, int], np.str_],
|
|
436
560
|
]: ...
|
|
437
561
|
|
|
438
562
|
|
|
@@ -444,7 +568,7 @@ def img2ascii( # type: ignore
|
|
|
444
568
|
char_set=None,
|
|
445
569
|
sort_glyphs=True,
|
|
446
570
|
*,
|
|
447
|
-
|
|
571
|
+
outarray=False,
|
|
448
572
|
):
|
|
449
573
|
"""Convert an image to a multiline ASCII string.
|
|
450
574
|
|
|
@@ -462,17 +586,15 @@ def img2ascii( # type: ignore
|
|
|
462
586
|
char_set : Iterable[str], optional
|
|
463
587
|
Characters to be mapped to greyscale values of 'img'.
|
|
464
588
|
|
|
465
|
-
sort_glyphs : {True, False,
|
|
589
|
+
sort_glyphs : {True, False, -1}
|
|
466
590
|
Specifies to sort `char_set` or leave it unsorted before mapping to greyscale.
|
|
467
591
|
|
|
468
592
|
Glyph bitmasks obtained from 'font' are compared when sorting the string.
|
|
469
593
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
ret_img : bool, default=False
|
|
473
|
-
Specifies to return both the output string and original RGB array.
|
|
594
|
+
`-1` specifies reverse sorting order.
|
|
474
595
|
|
|
475
|
-
|
|
596
|
+
outarray : bool, default=False
|
|
597
|
+
If True, returns the raw chararray. Otherwise, returns a string or list of strings.
|
|
476
598
|
|
|
477
599
|
Returns
|
|
478
600
|
-------
|
|
@@ -488,58 +610,19 @@ def img2ascii( # type: ignore
|
|
|
488
610
|
--------
|
|
489
611
|
ascii2img : Render an ASCII string as an image.
|
|
490
612
|
"""
|
|
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")
|
|
613
|
+
with _ConversionHandler(
|
|
614
|
+
font, factor=factor, char_set=char_set, sort_glyphs=sort_glyphs
|
|
615
|
+
) as h:
|
|
616
|
+
out = h.to_ascii(img)
|
|
617
|
+
if outarray is True:
|
|
618
|
+
return out
|
|
619
|
+
newlines = np.zeros((*out.shape[:-1], 1), dtype="<U1")
|
|
540
620
|
newlines[:-1] = "\n"
|
|
541
|
-
|
|
542
|
-
|
|
621
|
+
out = np.concatenate((out, newlines), axis=-1)
|
|
622
|
+
if out.ndim == 3:
|
|
623
|
+
return "".join(out.flat)
|
|
624
|
+
else:
|
|
625
|
+
return ["".join(x.flat) for x in out]
|
|
543
626
|
|
|
544
627
|
|
|
545
628
|
@tp.overload
|
|
@@ -555,12 +638,12 @@ def img2ansi(
|
|
|
555
638
|
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
556
639
|
*,
|
|
557
640
|
outarray: tp.Literal[False] = False,
|
|
558
|
-
) -> core.color_chain: ...
|
|
641
|
+
) -> core.color_chain | list[core.color_chain]: ...
|
|
559
642
|
|
|
560
643
|
|
|
561
644
|
@tp.overload
|
|
562
645
|
def img2ansi(
|
|
563
|
-
img:
|
|
646
|
+
img: _tp.RGBArray,
|
|
564
647
|
/,
|
|
565
648
|
font: _tp.FontArgType = ...,
|
|
566
649
|
factor: int = ...,
|
|
@@ -574,17 +657,52 @@ def img2ansi(
|
|
|
574
657
|
) -> _tp.ShapedNDArray[tuple[int, int], np.void]: ...
|
|
575
658
|
|
|
576
659
|
|
|
577
|
-
@
|
|
660
|
+
@tp.overload
|
|
578
661
|
def img2ansi(
|
|
579
|
-
img:
|
|
662
|
+
img: _tp.RGBArray3d,
|
|
580
663
|
/,
|
|
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] =
|
|
664
|
+
font: _tp.FontArgType = ...,
|
|
665
|
+
factor: int = ...,
|
|
666
|
+
char_set: tp.Optional[str] = ...,
|
|
667
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
668
|
+
ansi_type: tp.Optional[core.AnsiColorParam] = ...,
|
|
669
|
+
equalize: bool | tp.Literal["white_point"] = ...,
|
|
670
|
+
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
671
|
+
*,
|
|
672
|
+
outarray: tp.Literal[True],
|
|
673
|
+
) -> _tp.ShapedNDArray[tuple[int, int, int], np.void]: ...
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
@tp.overload
|
|
677
|
+
def img2ansi(
|
|
678
|
+
img: str | os.PathLike[str] | Image.Image,
|
|
679
|
+
/,
|
|
680
|
+
font: _tp.FontArgType = ...,
|
|
681
|
+
factor: int = ...,
|
|
682
|
+
char_set: tp.Optional[str] = ...,
|
|
683
|
+
sort_glyphs: bool | tp.Literal[-1] = ...,
|
|
684
|
+
ansi_type: tp.Optional[core.AnsiColorParam] = ...,
|
|
685
|
+
equalize: bool | tp.Literal["white_point"] = ...,
|
|
686
|
+
bg: tp.Optional[_tp.Int3Tuple | str] = ...,
|
|
687
|
+
*,
|
|
688
|
+
outarray: tp.Literal[True],
|
|
689
|
+
) -> tp.Union[
|
|
690
|
+
_tp.ShapedNDArray[tuple[int, int], np.void],
|
|
691
|
+
_tp.ShapedNDArray[tuple[int, int, int], np.void],
|
|
692
|
+
]: ...
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
@rgb_dispatch("bg")
|
|
696
|
+
def img2ansi( # type: ignore
|
|
697
|
+
img,
|
|
698
|
+
/,
|
|
699
|
+
font=uf.VGA437,
|
|
700
|
+
factor=200,
|
|
701
|
+
char_set=None,
|
|
702
|
+
sort_glyphs=True,
|
|
703
|
+
ansi_type=None,
|
|
704
|
+
equalize=False,
|
|
705
|
+
bg=None,
|
|
588
706
|
*,
|
|
589
707
|
outarray=False,
|
|
590
708
|
):
|
|
@@ -607,12 +725,12 @@ def img2ansi(
|
|
|
607
725
|
|
|
608
726
|
If None (default), the character set will be determined based on the 'font' parameter.
|
|
609
727
|
|
|
610
|
-
sort_glyphs : {True, False,
|
|
728
|
+
sort_glyphs : {True, False, -1}
|
|
611
729
|
Specifies to sort `char_set` or leave it unsorted before mapping to greyscale.
|
|
612
730
|
|
|
613
731
|
Glyph bitmasks obtained from 'font' are compared when sorting the string.
|
|
614
732
|
|
|
615
|
-
|
|
733
|
+
`-1` specifies reverse sorting order.
|
|
616
734
|
|
|
617
735
|
ansi_type : AnsiColorParam
|
|
618
736
|
ANSI color format to map the RGB values to.
|
|
@@ -656,30 +774,22 @@ def img2ansi(
|
|
|
656
774
|
ansi2img : Render an ANSI array as an image.
|
|
657
775
|
img2ascii : Used to obtain the base ASCII characters.
|
|
658
776
|
"""
|
|
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)
|
|
777
|
+
with _ConversionHandler(
|
|
778
|
+
font,
|
|
779
|
+
factor=factor,
|
|
780
|
+
char_set=char_set,
|
|
781
|
+
sort_glyphs=sort_glyphs,
|
|
782
|
+
ansi_type=ansi_type,
|
|
783
|
+
equalize=equalize,
|
|
784
|
+
bg=bg,
|
|
785
|
+
) as h:
|
|
786
|
+
out = h.to_ansi(img)
|
|
787
|
+
if outarray is True:
|
|
788
|
+
return out
|
|
789
|
+
elif out.ndim == 2:
|
|
790
|
+
return core.color_chain.fromarray(out)
|
|
791
|
+
else:
|
|
792
|
+
return [core.color_chain.fromarray(x) for x in out]
|
|
683
793
|
|
|
684
794
|
|
|
685
795
|
@rgb_dispatch("fg", "bg")
|
|
@@ -714,20 +824,20 @@ def ascii2img(
|
|
|
714
824
|
Returns
|
|
715
825
|
-------
|
|
716
826
|
ascii_img : Image
|
|
717
|
-
A `
|
|
827
|
+
A `Image.Image` object of the rendered ASCII string.
|
|
718
828
|
|
|
719
829
|
See Also
|
|
720
830
|
--------
|
|
721
831
|
img2ascii : Convert an image into an ASCII string.
|
|
722
832
|
"""
|
|
723
|
-
font =
|
|
833
|
+
font = ImageFont.truetype(get_font_object(font, retpath=True), font_size)
|
|
724
834
|
lines = s.split("\n")
|
|
725
835
|
n_rows, n_cols = map(len, (lines, lines[0]))
|
|
726
836
|
cw, ch = _get_bbox_shape(font)
|
|
727
837
|
iw, ih = (int(i * j) for i, j in zip((cw, ch), (n_cols, n_rows)))
|
|
728
838
|
r, g, b = tuple(map(int, bg))
|
|
729
|
-
img =
|
|
730
|
-
draw =
|
|
839
|
+
img = Image.new("RGB", (iw, ih), (r, g, b))
|
|
840
|
+
draw = ImageDraw.Draw(img)
|
|
731
841
|
y_offset = 0
|
|
732
842
|
for line in lines:
|
|
733
843
|
draw.text((0, y_offset), line, font=font, fill=fg)
|
|
@@ -740,7 +850,6 @@ def ansi2img(
|
|
|
740
850
|
arr: (
|
|
741
851
|
_tp.ShapedNDArray[tuple[int, int], np.void]
|
|
742
852
|
| core.color_chain
|
|
743
|
-
| list[core.color_chain]
|
|
744
853
|
| list[list[core.ColorStr]]
|
|
745
854
|
),
|
|
746
855
|
/,
|
|
@@ -772,7 +881,7 @@ def ansi2img(
|
|
|
772
881
|
Returns
|
|
773
882
|
-------
|
|
774
883
|
ansi_img : Image
|
|
775
|
-
The rendered ANSI array as an `
|
|
884
|
+
The rendered ANSI array as an `Image.Image` object.
|
|
776
885
|
|
|
777
886
|
Raises
|
|
778
887
|
------
|
|
@@ -792,7 +901,7 @@ def ansi2img(
|
|
|
792
901
|
if not arr.size:
|
|
793
902
|
raise ValueError("input array is empty")
|
|
794
903
|
|
|
795
|
-
font =
|
|
904
|
+
font = ImageFont.truetype(get_font_object(font, retpath=True), font_size)
|
|
796
905
|
bbox_h = _get_bbox_shape(font)[-1]
|
|
797
906
|
widths = np.asarray(
|
|
798
907
|
[[font.getbbox(c)[2] for c in x["char"]] for x in arr], dtype=np.uint32
|
|
@@ -813,10 +922,8 @@ def ansi2img(
|
|
|
813
922
|
if rgba:
|
|
814
923
|
mode = "RGBA"
|
|
815
924
|
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,)
|
|
925
|
+
*rgb_args, (subd1, subd2) = rgba_descr[-1]
|
|
926
|
+
rgba_descr[-1] = (*rgb_args, (subd1, subd2 + 1))
|
|
820
927
|
arr = arr.astype(rgba_descr)
|
|
821
928
|
arr["rgb"][..., 0, -1] = 0xFF
|
|
822
929
|
else:
|
|
@@ -827,8 +934,8 @@ def ansi2img(
|
|
|
827
934
|
arr["rgb"][mask, i, 0] = 1
|
|
828
935
|
arr["rgb"][mask, i, 1 : len(fill) + 1] = fill
|
|
829
936
|
|
|
830
|
-
img =
|
|
831
|
-
draw =
|
|
937
|
+
img = Image.new(mode, (iw, ih), bg_default)
|
|
938
|
+
draw = ImageDraw.Draw(img)
|
|
832
939
|
y_offset = 0
|
|
833
940
|
for y in range(arr.shape[0]):
|
|
834
941
|
x_offset = 0
|
|
@@ -860,36 +967,41 @@ def ansify(
|
|
|
860
967
|
fg: _tp.Int3Tuple | str = (170, 170, 170),
|
|
861
968
|
bg: _tp.Int3Tuple | str = (0, 0, 0),
|
|
862
969
|
):
|
|
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
|
-
),
|
|
970
|
+
arr = img2ansi(
|
|
971
|
+
img,
|
|
876
972
|
font,
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
973
|
+
factor=factor,
|
|
974
|
+
char_set=char_set,
|
|
975
|
+
ansi_type=ansi_type,
|
|
976
|
+
sort_glyphs=sort_glyphs,
|
|
977
|
+
equalize=equalize,
|
|
978
|
+
bg=bg,
|
|
979
|
+
outarray=True,
|
|
880
980
|
)
|
|
981
|
+
if arr.ndim == 4:
|
|
982
|
+
arr = arr[0]
|
|
983
|
+
assert _is_cc_array2d(arr)
|
|
984
|
+
return ansi2img(arr, font, font_size=font_size, fg_default=fg, bg_default=bg)
|
|
881
985
|
|
|
882
986
|
|
|
883
987
|
def _is_array(obj: tp.Any, /) -> tp.TypeGuard[np.ndarray]:
|
|
884
988
|
return isinstance(obj, np.ndarray)
|
|
885
989
|
|
|
886
990
|
|
|
887
|
-
def
|
|
888
|
-
|
|
991
|
+
def _is_cc_array(
|
|
992
|
+
obj: tp.Any, /
|
|
993
|
+
) -> tp.TypeGuard[_tp.ShapedNDArray[tuple[int, ...], np.void]]:
|
|
994
|
+
return _is_array(obj) and np.issubdtype(obj.dtype, core.color_chain.dtype)
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
def _is_cc_array2d(
|
|
998
|
+
obj: tp.Any, /
|
|
999
|
+
) -> tp.TypeGuard[_tp.ShapedNDArray[tuple[int, int], np.void]]:
|
|
1000
|
+
return _is_cc_array(obj) and obj.ndim == 2
|
|
889
1001
|
|
|
890
1002
|
|
|
891
|
-
def _is_image(obj: tp.Any, /) -> tp.TypeGuard[
|
|
892
|
-
return isinstance(obj,
|
|
1003
|
+
def _is_image(obj: tp.Any, /) -> tp.TypeGuard[Image.Image]:
|
|
1004
|
+
return isinstance(obj, Image.Image)
|
|
893
1005
|
|
|
894
1006
|
|
|
895
1007
|
@lru_cache(maxsize=1)
|
|
@@ -1235,7 +1347,7 @@ def render_ans(
|
|
|
1235
1347
|
font_size: int = 16,
|
|
1236
1348
|
*,
|
|
1237
1349
|
bg_default: _tp.Int3Tuple | _tp.TupleOf4[int] | str = (0, 0, 0),
|
|
1238
|
-
) ->
|
|
1350
|
+
) -> Image.Image:
|
|
1239
1351
|
"""Return an image render of an ANS file.
|
|
1240
1352
|
|
|
1241
1353
|
Parameters
|
|
@@ -1278,7 +1390,7 @@ def render_ans(
|
|
|
1278
1390
|
|
|
1279
1391
|
|
|
1280
1392
|
def otsu_mask(
|
|
1281
|
-
img: _tp.MatrixLike[np.uint8] | cv.typing.MatLike |
|
|
1393
|
+
img: _tp.MatrixLike[np.uint8] | cv.typing.MatLike | Image.Image,
|
|
1282
1394
|
) -> _tp.MatrixLike[np.uint8]:
|
|
1283
1395
|
img = np.asarray(img, dtype=np.uint8)
|
|
1284
1396
|
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.0}/chromatic_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{chromatic_python-0.7.4 → chromatic_python-0.8.0}/chromatic_python.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{chromatic_python-0.7.4 → chromatic_python-0.8.0}/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
|