Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 895530f

Browse files
authoredApr 3, 2022
Better Visualization Functions (#129)
* Not drawing outlines when box_width is 0 * Support colors in draw_box * Better naming & variable annotation * support seeting box_alpha & box_width as a list
1 parent e7c62d6 commit 895530f

File tree

2 files changed

+145
-66
lines changed

2 files changed

+145
-66
lines changed
 

‎src/layoutparser/visualization.py

+98-47
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from typing import List, Union, Dict, Any, Tuple
15+
from typing import List, Optional, Union, Dict, Any, Tuple, Dict
1616
import functools
1717
import os
1818
import sys
@@ -223,18 +223,19 @@ def draw_transparent_box(
223223

224224
@image_loader
225225
def draw_box(
226-
canvas,
227-
layout,
228-
box_width=None,
229-
box_alpha=0,
230-
color_map=None,
231-
show_element_id=False,
232-
show_element_type=False,
233-
id_font_size=None,
234-
id_font_path=None,
235-
id_text_color=None,
236-
id_text_background_color=None,
237-
id_text_background_alpha=1,
226+
canvas: Image.Image,
227+
layout: Layout,
228+
box_width: Optional[Union[List[int], int]] = None,
229+
box_alpha: Optional[Union[List[float], float]] = None,
230+
box_color: Optional[Union[List[str], str]] = None,
231+
color_map: Optional[Dict] = None,
232+
show_element_id: bool = False,
233+
show_element_type: bool = False,
234+
id_font_size: Optional[int] = None,
235+
id_font_path: Optional[str] = None,
236+
id_text_color: Optional[str] = None,
237+
id_text_background_color: Optional[str] = None,
238+
id_text_background_alpha: Optional[float] = 1,
238239
):
239240
"""Draw the layout region on the input canvas(image).
240241
@@ -243,15 +244,29 @@ def draw_box(
243244
The canvas to draw the layout boxes.
244245
layout (:obj:`Layout` or :obj:`list`):
245246
The layout of the canvas to show.
246-
box_width (:obj:`int`, optional):
247+
box_width (:obj:`int` or :obj:`List[int]`, optional):
247248
Set to change the width of the drawn layout box boundary.
248249
Defaults to None, when the boundary is automatically
249250
calculated as the the :const:`DEFAULT_BOX_WIDTH_RATIO`
250251
* the maximum of (height, width) of the canvas.
251-
box_alpha (:obj:`float`, optional):
252-
A float range from 0 to 1. Set to change the alpha of the
253-
drawn layout box.
252+
If box_with is a list, it will assign different widths to
253+
the corresponding layout object, and should have the same
254+
length as the number of blocks in `layout`.
255+
box_alpha (:obj:`float` or :obj:`List[float]`, optional):
256+
A float or list of floats ranging from 0 to 1. Set to change
257+
the alpha of the drawn layout box.
254258
Defaults to 0 - the layout box will be fully transparent.
259+
If box_alpha is a list of floats, it will assign different
260+
alphas to the corresponding layout object, and should have
261+
the same length as the number of blocks in `layout`.
262+
box_color (:obj:`str` or :obj:`List[str]`, optional):
263+
A string or a list of strings for box colors, e.g.,
264+
`['red', 'green', 'blue']` or `'red'`.
265+
If box_color is a list of strings, it will assign different
266+
colors to the corresponding layout object, and should have
267+
the same length as the number of blocks in `layout`.
268+
Defaults to None. When `box_color` is set, it will override the
269+
`color_map`.
255270
color_map (dict, optional):
256271
A map from `block.type` to the colors, e.g., `{1: 'red'}`.
257272
You can set it to `{}` to use only the
@@ -290,9 +305,6 @@ def draw_box(
290305
A Image object containing the `layout` draw upon the input `canvas`.
291306
"""
292307

293-
assert 0 <= box_alpha <= 1, ValueError(
294-
f"The box_alpha value {box_alpha} is not within range [0,1]."
295-
)
296308
assert 0 <= id_text_background_alpha <= 1, ValueError(
297309
f"The id_text_background_alpha value {id_text_background_alpha} is not within range [0,1]."
298310
)
@@ -302,30 +314,66 @@ def draw_box(
302314
id_text_background_color = id_text_background_color or DEFAULT_TEXT_BACKGROUND
303315
id_text_color = id_text_color or DEFAULT_TEXT_COLOR
304316

305-
if box_width is None:
306-
box_width = _calculate_default_box_width(canvas)
307-
308317
if show_element_id or show_element_type:
309318
font_obj = _create_font_object(id_font_size, id_font_path)
310319

311-
if color_map is None:
312-
all_types = set([b.type for b in layout if hasattr(b, "type")])
313-
color_map = _create_color_palette(all_types)
320+
if box_alpha is None:
321+
box_alpha = [0]
322+
else:
323+
if isinstance(box_alpha, (float, int)):
324+
box_alpha = [box_alpha] * len(layout)
314325

315-
for idx, ele in enumerate(layout):
326+
if len(box_alpha) != len(layout):
327+
raise ValueError(
328+
f"The number of alphas {len(box_alpha)} is not equal to the number of blocks {len(layout)}"
329+
)
330+
if not all(0 <= a <= 1 for a in box_alpha):
331+
raise ValueError(
332+
f"The box_alpha value {box_alpha} is not within range [0,1]."
333+
)
316334

317-
if isinstance(ele, Interval):
318-
ele = ele.put_on_canvas(canvas)
335+
if box_width is None:
336+
box_width = _calculate_default_box_width(canvas)
337+
box_width = [box_width] * len(layout)
338+
else:
339+
if isinstance(box_width, (float, int)):
340+
box_width = [box_width] * len(layout)
341+
342+
if len(box_width) != len(layout):
343+
raise ValueError(
344+
f"The number of widths {len(box_width)} is not equal to the number of blocks {len(layout)}"
345+
)
319346

320-
outline_color = (
347+
if box_color is None:
348+
if color_map is None:
349+
all_types = set([b.type for b in layout if hasattr(b, "type")])
350+
color_map = _create_color_palette(all_types)
351+
box_color = [
321352
DEFAULT_OUTLINE_COLOR
322353
if not isinstance(ele, TextBlock)
323354
else color_map.get(ele.type, DEFAULT_OUTLINE_COLOR)
324-
)
355+
for ele in layout
356+
]
357+
else:
358+
if isinstance(box_color, str):
359+
box_color = [box_color] * len(layout)
325360

326-
_draw_box_outline_on_handler(draw, ele, outline_color, box_width)
361+
if len(box_color) != len(layout):
362+
raise ValueError(
363+
f"The number of colors {len(box_color)} is not equal to the number of blocks {len(layout)}"
364+
)
327365

328-
_draw_transparent_box_on_handler(draw, ele, outline_color, box_alpha)
366+
for idx, (ele, color, alpha, width) in enumerate(
367+
zip(layout, box_color, box_alpha, box_width)
368+
):
369+
370+
if isinstance(ele, Interval):
371+
ele = ele.put_on_canvas(canvas)
372+
373+
if width > 0:
374+
_draw_box_outline_on_handler(draw, ele, color, width)
375+
376+
_draw_transparent_box_on_handler(draw, ele, color, alpha)
329377

330378
if show_element_id or show_element_type:
331379
text = ""
@@ -365,18 +413,18 @@ def draw_box(
365413
def draw_text(
366414
canvas,
367415
layout,
368-
arrangement="lr",
369-
font_size=None,
370-
font_path=None,
371-
text_color=None,
372-
text_background_color=None,
373-
text_background_alpha=1,
374-
vertical_text=False,
375-
with_box_on_text=False,
376-
text_box_width=None,
377-
text_box_color=None,
378-
text_box_alpha=0,
379-
with_layout=False,
416+
arrangement: str = "lr",
417+
font_size: Optional[int] = None,
418+
font_path: Optional[str] = None,
419+
text_color: Optional[str] = None,
420+
text_background_color: Optional[str] = None,
421+
text_background_alpha: Optional[float] = None,
422+
vertical_text: bool = False,
423+
with_box_on_text: bool = False,
424+
text_box_width: Optional[int] = None,
425+
text_box_color: Optional[str] = None,
426+
text_box_alpha: Optional[float] = None,
427+
with_layout: bool = False,
380428
**kwargs,
381429
):
382430
"""Draw the (detected) text in the `layout` according to
@@ -392,7 +440,6 @@ def draw_text(
392440
image canvas:
393441
* `lr` - left and right
394442
* `ud` - up and down
395-
396443
Defaults to 'lr'.
397444
font_size (:obj:`str`, optional):
398445
Set to change the size of the font used for
@@ -435,7 +482,6 @@ def draw_text(
435482
Set to change the color of the drawn layout box boundary.
436483
Defaults to None, when the color is set to
437484
:const:`DEFAULT_OUTLINE_COLOR`.
438-
439485
with_layout (bool, optional):
440486
Whether to draw the layout boxes on the input (image) canvas.
441487
Defaults to False.
@@ -447,6 +493,11 @@ def draw_text(
447493
A Image object containing the drawn text from `layout`.
448494
"""
449495

496+
if text_background_alpha is None:
497+
text_background_alpha = 1
498+
if text_box_alpha is None:
499+
text_box_alpha = 0
500+
450501
assert 0 <= text_background_alpha <= 1, ValueError(
451502
f"The text_background_color value {text_background_alpha} is not within range [0,1]."
452503
)

‎tests/test_visualization.py

+47-19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import pytest
16+
1517
from layoutparser.elements import *
1618
from layoutparser.ocr import *
1719
from layoutparser.visualization import *
@@ -30,26 +32,52 @@ def test_viz():
3032
draw_box(image, Layout([]))
3133
draw_text(image, Layout([]))
3234

33-
draw_box(
34-
image,
35-
Layout(
36-
[
37-
Interval(0, 10, axis="x"),
38-
Rectangle(0, 50, 100, 80),
39-
Quadrilateral(np.array([[10, 10], [30, 40], [90, 40], [10, 20]])),
40-
]
41-
),
35+
layout = Layout(
36+
[
37+
Interval(0, 10, axis="x"),
38+
Rectangle(0, 50, 100, 80),
39+
Quadrilateral(np.array([[10, 10], [30, 40], [90, 40], [10, 20]])),
40+
]
4241
)
4342

44-
draw_text(
43+
draw_box(image, layout)
44+
draw_text(image, layout)
45+
46+
# Test colors
47+
draw_box(image, layout, box_color=["red", "green", "blue"])
48+
draw_box(image, layout, box_color="red")
49+
50+
draw_text(image, layout, box_color=["red", "green", "blue"])
51+
with pytest.raises(ValueError):
52+
draw_box(image, layout, box_color=["red", "green", "blue", "yellow"])
53+
with pytest.raises(ValueError):
54+
draw_text(
55+
image,
56+
layout,
57+
box_color=["red", "green", "blue", "yellow"],
58+
with_layout=True,
59+
)
60+
61+
# Test alphas
62+
draw_box(image, layout, box_alpha=0)
63+
draw_box(image, layout, box_alpha=[0.1, 0.2, 0.3])
64+
with pytest.raises(ValueError):
65+
draw_box(image, layout, box_color=[0.1, 0.2, 0.3, 0.5])
66+
with pytest.raises(ValueError):
67+
draw_box(image, layout, box_color=[0.1, 0.2, 0.3, 1.5])
68+
69+
# Test widths
70+
draw_box(image, layout, box_width=1)
71+
draw_box(image, layout, box_width=[1, 2, 3])
72+
with pytest.raises(ValueError):
73+
draw_box(image, layout, box_width=[1, 2, 3, 4])
74+
75+
draw_box(
4576
image,
46-
Layout(
47-
[
48-
Interval(0, 10, axis="x"),
49-
Rectangle(0, 50, 100, 80),
50-
Quadrilateral(np.array([[10, 10], [30, 40], [90, 40], [10, 20]])),
51-
]
52-
),
77+
layout,
78+
box_alpha=[0.1, 0.2, 0.3],
79+
box_width=[1, 2, 3],
80+
box_color=["red", "green", "blue"],
5381
)
5482

5583
for idx, level in enumerate(
@@ -82,8 +110,8 @@ def test_viz():
82110
show_element_id=True,
83111
id_font_size=8,
84112
box_alpha=0.25,
85-
id_text_background_alpha=0.25
113+
id_text_background_alpha=0.25,
86114
)
87115

88116
draw_box(image, layout)
89-
draw_text(image, layout)
117+
draw_text(image, layout)

0 commit comments

Comments
 (0)
Please sign in to comment.