shunk031 commited on
Commit
e6ddeda
·
1 Parent(s): 47c6f77

deploy: 63a85616f5fc427cf1e1e7b425293131f2fce2b8

Browse files
Files changed (3) hide show
  1. README.md +166 -1
  2. layout-utility.py +10 -6
  3. requirements.txt +136 -89
README.md CHANGED
@@ -8,4 +8,169 @@ sdk_version: 4.36.1
8
  app_file: app.py
9
  pinned: false
10
  ---
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  app_file: app.py
9
  pinned: false
10
  ---
11
+
12
+ # Layout Utility
13
+
14
+ ## Description
15
+
16
+ The Layout Utility metric evaluates how efficiently a layout utilizes suitable space for element placement. It measures the utilization rate of regions that are appropriate for arranging elements, as determined by the negative (inverted) saliency map. This metric helps assess whether layouts make effective use of available non-salient (less visually important) space.
17
+
18
+ ## What It Measures
19
+
20
+ This metric computes:
21
+
22
+ - **Space utilization**: How much of the suitable (low-saliency) space is occupied by elements
23
+ - **Placement efficiency**: Whether elements are placed in appropriate regions
24
+ - **Canvas coverage**: How well the layout fills available design space
25
+
26
+ Higher scores indicate better utilization of suitable space, though extremely high scores might indicate overcrowding.
27
+
28
+ ## Metric Details
29
+
30
+ - Uses the **negative/inverted saliency map** (S') to identify suitable placement regions
31
+ - Suitable space = regions with LOW visual importance (complement of salient areas)
32
+ - Computes the ratio of element-covered suitable space to total suitable space
33
+ - From PosterLayout (Hsu et al., CVPR 2023) for content-aware poster design
34
+ - Higher utility means elements effectively use non-salient space
35
+
36
+ ## Usage
37
+
38
+ ### Installation
39
+
40
+ ```bash
41
+ pip install evaluate
42
+ ```
43
+
44
+ ### Basic Example
45
+
46
+ ```python
47
+ import evaluate
48
+ import numpy as np
49
+
50
+ # Load the metric with canvas dimensions
51
+ metric = evaluate.load(
52
+ "creative-graphic-design/layout-utility",
53
+ canvas_width=360,
54
+ canvas_height=504
55
+ )
56
+
57
+ # Prepare data
58
+ predictions = np.random.rand(1, 25, 4) # normalized ltrb coordinates
59
+ gold_labels = np.random.randint(0, 4, size=(1, 25)) # class labels
60
+ # Paths to saliency map images (grayscale, 0-255)
61
+ saliency_maps_1 = ["path/to/saliency_map_1.png"]
62
+ saliency_maps_2 = ["path/to/saliency_map_2.png"]
63
+
64
+ score = metric.compute(
65
+ predictions=predictions,
66
+ gold_labels=gold_labels,
67
+ saliency_maps_1=saliency_maps_1,
68
+ saliency_maps_2=saliency_maps_2
69
+ )
70
+ print(score)
71
+ ```
72
+
73
+ ### Batch Processing Example
74
+
75
+ ```python
76
+ import evaluate
77
+
78
+ # Load the metric
79
+ metric = evaluate.load(
80
+ "creative-graphic-design/layout-utility",
81
+ canvas_width=360,
82
+ canvas_height=504
83
+ )
84
+
85
+ # Batch processing
86
+ batch_size = 128
87
+ predictions = np.random.rand(batch_size, 25, 4)
88
+ gold_labels = np.random.randint(0, 4, size=(batch_size, 25))
89
+ saliency_maps_1 = [f"path/to/saliency_{i}_1.png" for i in range(batch_size)]
90
+ saliency_maps_2 = [f"path/to/saliency_{i}_2.png" for i in range(batch_size)]
91
+
92
+ score = metric.compute(
93
+ predictions=predictions,
94
+ gold_labels=gold_labels,
95
+ saliency_maps_1=saliency_maps_1,
96
+ saliency_maps_2=saliency_maps_2
97
+ )
98
+ print(score)
99
+ ```
100
+
101
+ ## Parameters
102
+
103
+ ### Initialization Parameters
104
+
105
+ - **canvas_width** (`int`, required): Width of the canvas in pixels
106
+ - **canvas_height** (`int`, required): Height of the canvas in pixels
107
+
108
+ ### Computation Parameters
109
+
110
+ - **predictions** (`list` of `lists` of `float`): Normalized bounding boxes in ltrb format (0.0 to 1.0)
111
+ - **gold_labels** (`list` of `lists` of `int`): Class labels for each element (0 = padding)
112
+ - **saliency_maps_1** (`list` of `str`): File paths to first set of saliency map images
113
+ - **saliency_maps_2** (`list` of `str`): File paths to second set of saliency map images
114
+
115
+ **Note**:
116
+
117
+ - Saliency maps should be grayscale images (0-255) where brighter = more salient/important
118
+ - The metric internally inverts these to find suitable (non-salient) placement regions
119
+ - Very small elements (< 0.1% of canvas) are filtered out
120
+
121
+ ## Returns
122
+
123
+ Returns a `float` value representing the utilization rate of suitable space (range: 0.0 to 1.0).
124
+
125
+ ## Interpretation
126
+
127
+ - **Higher is generally better** (range: 0.0 to 1.0)
128
+ - **Value ~1.0**: Very high utilization of suitable space (may indicate dense layout)
129
+ - **Value 0.6-0.9**: Good space utilization
130
+ - **Value 0.3-0.6**: Moderate utilization, room for more elements
131
+ - **Value 0.0-0.3**: Low utilization, underused suitable space
132
+ - **Value ~0.0**: Very sparse layout, most suitable space unused
133
+
134
+ ### Use Cases
135
+
136
+ - **Content-aware layout generation**: Evaluate if layouts efficiently use available space
137
+ - **Poster/flyer design**: Assess whether designs make good use of non-salient regions
138
+ - **Space efficiency analysis**: Compare layout density across different designs
139
+ - **Design quality**: Identify layouts that are too sparse or potentially too dense
140
+
141
+ ### Key Insights
142
+
143
+ - **Balance is important**: Neither too sparse nor too dense is ideal
144
+ - **Context-dependent**: Optimal utility varies by design type (minimalist vs. information-dense)
145
+ - **Suitable space concept**: Based on inverted saliency - where elements CAN go, not where they SHOULD avoid
146
+ - **Combine with occlusion**: High utility + low occlusion = good space utilization without blocking important content
147
+ - **Not absolute quality**: High utility alone doesn't guarantee good design
148
+
149
+ ### Interpretation Considerations
150
+
151
+ - Very high utility (>0.9) might indicate overcrowding
152
+ - Very low utility (<0.2) might indicate wasteful use of space
153
+ - Optimal range typically 0.4-0.8 depending on design intent
154
+
155
+ ## Citations
156
+
157
+ ```bibtex
158
+ @inproceedings{hsu2023posterlayout,
159
+ title={Posterlayout: A new benchmark and approach for content-aware visual-textual presentation layout},
160
+ author={Hsu, Hsiao Yuan and He, Xiangteng and Peng, Yuxin and Kong, Hao and Zhang, Qing},
161
+ booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
162
+ pages={6018--6026},
163
+ year={2023}
164
+ }
165
+ ```
166
+
167
+ ## References
168
+
169
+ - **Paper**: [PosterLayout (Hsu et al., CVPR 2023)](https://arxiv.org/abs/2303.15937)
170
+ - **Reference Implementation**: [PosterLayout eval.py](https://github.com/PKU-ICST-MIPL/PosterLayout-CVPR2023/blob/main/eval.py#L144-L171)
171
+
172
+ ## Related Metrics
173
+
174
+ - [Layout Occlusion](../layout_occlusion/): Evaluates coverage of salient (important) regions
175
+ - [Layout Validity](../layout_validity/): Checks basic validity constraints
176
+ - [Layout Unreadability](../layout_unreadability/): Evaluates text placement quality
layout-utility.py CHANGED
@@ -5,6 +5,7 @@ import datasets as ds
5
  import evaluate
6
  import numpy as np
7
  import numpy.typing as npt
 
8
  from PIL import Image
9
 
10
  _DESCRIPTION = r"""\
@@ -26,6 +27,7 @@ _CITATION = """\
26
  """
27
 
28
 
 
29
  class LayoutUtility(evaluate.Metric):
30
  def __init__(
31
  self,
@@ -64,10 +66,10 @@ class LayoutUtility(evaluate.Metric):
64
  filepath = filepath[0]
65
 
66
  map_pil = Image.open(filepath) # type: ignore
67
- map_pil = map_pil.convert("L")
68
 
69
  if map_pil.size != (self.canvas_width, self.canvas_height):
70
- map_pil = map_pil.resize((self.canvas_width, self.canvas_height))
71
 
72
  map_arr = np.array(map_pil)
73
  map_arr = map_arr / 255.0
@@ -111,7 +113,7 @@ class LayoutUtility(evaluate.Metric):
111
  predictions=predictions, gold_labels=gold_labels
112
  )
113
 
114
- score = 0
115
 
116
  assert (
117
  len(predictions)
@@ -119,7 +121,6 @@ class LayoutUtility(evaluate.Metric):
119
  == len(saliency_maps_1)
120
  == len(saliency_maps_2)
121
  )
122
- num_predictions = len(predictions)
123
  it = zip(predictions, gold_labels, saliency_maps_1, saliency_maps_2)
124
 
125
  for prediction, gold_label, smap_1, smap_2 in it:
@@ -145,5 +146,8 @@ class LayoutUtility(evaluate.Metric):
145
  total_utils = np.sum(c_smap * cal_mask)
146
 
147
  if total_not_sal and total_utils:
148
- score += total_utils / total_not_sal
149
- return score / num_predictions
 
 
 
 
5
  import evaluate
6
  import numpy as np
7
  import numpy.typing as npt
8
+ from evaluate.utils.file_utils import add_start_docstrings
9
  from PIL import Image
10
 
11
  _DESCRIPTION = r"""\
 
27
  """
28
 
29
 
30
+ @add_start_docstrings(_DESCRIPTION, _KWARGS_DESCRIPTION)
31
  class LayoutUtility(evaluate.Metric):
32
  def __init__(
33
  self,
 
66
  filepath = filepath[0]
67
 
68
  map_pil = Image.open(filepath) # type: ignore
69
+ map_pil = map_pil.convert("L") # type: ignore
70
 
71
  if map_pil.size != (self.canvas_width, self.canvas_height):
72
+ map_pil = map_pil.resize((self.canvas_width, self.canvas_height)) # type: ignore
73
 
74
  map_arr = np.array(map_pil)
75
  map_arr = map_arr / 255.0
 
113
  predictions=predictions, gold_labels=gold_labels
114
  )
115
 
116
+ score = []
117
 
118
  assert (
119
  len(predictions)
 
121
  == len(saliency_maps_1)
122
  == len(saliency_maps_2)
123
  )
 
124
  it = zip(predictions, gold_labels, saliency_maps_1, saliency_maps_2)
125
 
126
  for prediction, gold_label, smap_1, smap_2 in it:
 
146
  total_utils = np.sum(c_smap * cal_mask)
147
 
148
  if total_not_sal and total_utils:
149
+ # score += total_utils / total_not_sal
150
+ score.append(total_utils / total_not_sal)
151
+
152
+ # return score / num_predictions
153
+ return np.mean(score)
requirements.txt CHANGED
@@ -1,89 +1,136 @@
1
- aiofiles==23.2.1 ; python_version >= "3.9" and python_version < "4.0"
2
- aiohttp==3.9.3 ; python_version >= "3.9" and python_version < "4.0"
3
- aiosignal==1.3.1 ; python_version >= "3.9" and python_version < "4.0"
4
- altair==5.2.0 ; python_version >= "3.9" and python_version < "4.0"
5
- annotated-types==0.6.0 ; python_version >= "3.9" and python_version < "4.0"
6
- anyio==4.2.0 ; python_version >= "3.9" and python_version < "4.0"
7
- arrow==1.3.0 ; python_version >= "3.9" and python_version < "4.0"
8
- async-timeout==4.0.3 ; python_version >= "3.9" and python_version < "3.11"
9
- attrs==23.2.0 ; python_version >= "3.9" and python_version < "4.0"
10
- binaryornot==0.4.4 ; python_version >= "3.9" and python_version < "4.0"
11
- certifi==2024.2.2 ; python_version >= "3.9" and python_version < "4.0"
12
- chardet==5.2.0 ; python_version >= "3.9" and python_version < "4.0"
13
- charset-normalizer==3.3.2 ; python_version >= "3.9" and python_version < "4.0"
14
- click==8.1.7 ; python_version >= "3.9" and python_version < "4.0"
15
- colorama==0.4.6 ; python_version >= "3.9" and python_version < "4.0"
16
- contourpy==1.2.0 ; python_version >= "3.9" and python_version < "4.0"
17
- cookiecutter==2.5.0 ; python_version >= "3.9" and python_version < "4.0"
18
- cycler==0.12.1 ; python_version >= "3.9" and python_version < "4.0"
19
- datasets==2.17.0 ; python_version >= "3.9" and python_version < "4.0"
20
- dill==0.3.8 ; python_version >= "3.9" and python_version < "4.0"
21
- evaluate[template]==0.4.1 ; python_version >= "3.9" and python_version < "4.0"
22
- exceptiongroup==1.2.0 ; python_version >= "3.9" and python_version < "3.11"
23
- fastapi==0.109.2 ; python_version >= "3.9" and python_version < "4.0"
24
- ffmpy==0.3.1 ; python_version >= "3.9" and python_version < "4.0"
25
- filelock==3.13.1 ; python_version >= "3.9" and python_version < "4.0"
26
- fonttools==4.48.1 ; python_version >= "3.9" and python_version < "4.0"
27
- frozenlist==1.4.1 ; python_version >= "3.9" and python_version < "4.0"
28
- fsspec==2023.10.0 ; python_version >= "3.9" and python_version < "4.0"
29
- fsspec[http]==2023.10.0 ; python_version >= "3.9" and python_version < "4.0"
30
- gradio-client==0.10.0 ; python_version >= "3.9" and python_version < "4.0"
31
- gradio==4.18.0 ; python_version >= "3.9" and python_version < "4.0"
32
- h11==0.14.0 ; python_version >= "3.9" and python_version < "4.0"
33
- httpcore==1.0.2 ; python_version >= "3.9" and python_version < "4.0"
34
- httpx==0.26.0 ; python_version >= "3.9" and python_version < "4.0"
35
- huggingface-hub==0.20.3 ; python_version >= "3.9" and python_version < "4.0"
36
- idna==3.6 ; python_version >= "3.9" and python_version < "4.0"
37
- importlib-resources==6.1.1 ; python_version >= "3.9" and python_version < "4.0"
38
- jinja2==3.1.3 ; python_version >= "3.9" and python_version < "4.0"
39
- jsonschema-specifications==2023.12.1 ; python_version >= "3.9" and python_version < "4.0"
40
- jsonschema==4.21.1 ; python_version >= "3.9" and python_version < "4.0"
41
- kiwisolver==1.4.5 ; python_version >= "3.9" and python_version < "4.0"
42
- markdown-it-py==3.0.0 ; python_version >= "3.9" and python_version < "4.0"
43
- markupsafe==2.1.5 ; python_version >= "3.9" and python_version < "4.0"
44
- matplotlib==3.8.2 ; python_version >= "3.9" and python_version < "4.0"
45
- mdurl==0.1.2 ; python_version >= "3.9" and python_version < "4.0"
46
- multidict==6.0.5 ; python_version >= "3.9" and python_version < "4.0"
47
- multiprocess==0.70.16 ; python_version >= "3.9" and python_version < "4.0"
48
- numpy==1.26.4 ; python_version >= "3.9" and python_version < "4.0"
49
- orjson==3.9.13 ; python_version >= "3.9" and python_version < "4.0"
50
- packaging==23.2 ; python_version >= "3.9" and python_version < "4.0"
51
- pandas==2.2.0 ; python_version >= "3.9" and python_version < "4.0"
52
- pillow==10.2.0 ; python_version >= "3.9" and python_version < "4.0"
53
- pyarrow-hotfix==0.6 ; python_version >= "3.9" and python_version < "4.0"
54
- pyarrow==15.0.0 ; python_version >= "3.9" and python_version < "4.0"
55
- pydantic-core==2.16.2 ; python_version >= "3.9" and python_version < "4.0"
56
- pydantic==2.6.1 ; python_version >= "3.9" and python_version < "4.0"
57
- pydub==0.25.1 ; python_version >= "3.9" and python_version < "4.0"
58
- pygments==2.17.2 ; python_version >= "3.9" and python_version < "4.0"
59
- pyparsing==3.1.1 ; python_version >= "3.9" and python_version < "4.0"
60
- python-dateutil==2.8.2 ; python_version >= "3.9" and python_version < "4.0"
61
- python-multipart==0.0.9 ; python_version >= "3.9" and python_version < "4.0"
62
- python-slugify==8.0.4 ; python_version >= "3.9" and python_version < "4.0"
63
- pytz==2024.1 ; python_version >= "3.9" and python_version < "4.0"
64
- pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "4.0"
65
- referencing==0.33.0 ; python_version >= "3.9" and python_version < "4.0"
66
- requests==2.31.0 ; python_version >= "3.9" and python_version < "4.0"
67
- responses==0.18.0 ; python_version >= "3.9" and python_version < "4.0"
68
- rich==13.7.0 ; python_version >= "3.9" and python_version < "4.0"
69
- rpds-py==0.17.1 ; python_version >= "3.9" and python_version < "4.0"
70
- ruff==0.2.1 ; python_version >= "3.9" and python_version < "4.0"
71
- semantic-version==2.10.0 ; python_version >= "3.9" and python_version < "4.0"
72
- shellingham==1.5.4 ; python_version >= "3.9" and python_version < "4.0"
73
- six==1.16.0 ; python_version >= "3.9" and python_version < "4.0"
74
- sniffio==1.3.0 ; python_version >= "3.9" and python_version < "4.0"
75
- starlette==0.36.3 ; python_version >= "3.9" and python_version < "4.0"
76
- text-unidecode==1.3 ; python_version >= "3.9" and python_version < "4.0"
77
- tomlkit==0.12.0 ; python_version >= "3.9" and python_version < "4.0"
78
- toolz==0.12.1 ; python_version >= "3.9" and python_version < "4.0"
79
- tqdm==4.66.2 ; python_version >= "3.9" and python_version < "4.0"
80
- typer[all]==0.9.0 ; python_version >= "3.9" and python_version < "4.0"
81
- types-python-dateutil==2.8.19.20240106 ; python_version >= "3.9" and python_version < "4.0"
82
- typing-extensions==4.9.0 ; python_version >= "3.9" and python_version < "4.0"
83
- tzdata==2024.1 ; python_version >= "3.9" and python_version < "4.0"
84
- urllib3==2.2.0 ; python_version >= "3.9" and python_version < "4.0"
85
- uvicorn==0.27.1 ; python_version >= "3.9" and python_version < "4.0"
86
- websockets==11.0.3 ; python_version >= "3.9" and python_version < "4.0"
87
- xxhash==3.4.1 ; python_version >= "3.9" and python_version < "4.0"
88
- yarl==1.9.4 ; python_version >= "3.9" and python_version < "4.0"
89
- zipp==3.17.0 ; python_version >= "3.9" and python_version < "3.10"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv export --package layout_utility --no-dev --no-hashes --format requirements-txt
3
+ aiohappyeyeballs==2.6.1
4
+ # via aiohttp
5
+ aiohttp==3.13.2
6
+ # via fsspec
7
+ aiosignal==1.4.0
8
+ # via aiohttp
9
+ anyio==4.12.0
10
+ # via httpx
11
+ attrs==25.4.0
12
+ # via aiohttp
13
+ certifi==2025.11.12
14
+ # via
15
+ # httpcore
16
+ # httpx
17
+ # requests
18
+ charset-normalizer==3.4.4
19
+ # via requests
20
+ click==8.3.1
21
+ # via typer-slim
22
+ colorama==0.4.6 ; sys_platform == 'win32'
23
+ # via
24
+ # click
25
+ # tqdm
26
+ datasets==4.4.2
27
+ # via evaluate
28
+ dill==0.4.0
29
+ # via
30
+ # datasets
31
+ # evaluate
32
+ # multiprocess
33
+ evaluate==0.4.6
34
+ # via layout-utility
35
+ filelock==3.20.1
36
+ # via
37
+ # datasets
38
+ # huggingface-hub
39
+ frozenlist==1.8.0
40
+ # via
41
+ # aiohttp
42
+ # aiosignal
43
+ fsspec==2025.10.0
44
+ # via
45
+ # datasets
46
+ # evaluate
47
+ # huggingface-hub
48
+ h11==0.16.0
49
+ # via httpcore
50
+ hf-xet==1.2.0 ; platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
51
+ # via huggingface-hub
52
+ httpcore==1.0.9
53
+ # via httpx
54
+ httpx==0.28.1
55
+ # via
56
+ # datasets
57
+ # huggingface-hub
58
+ huggingface-hub==1.2.3
59
+ # via
60
+ # datasets
61
+ # evaluate
62
+ idna==3.11
63
+ # via
64
+ # anyio
65
+ # httpx
66
+ # requests
67
+ # yarl
68
+ multidict==6.7.0
69
+ # via
70
+ # aiohttp
71
+ # yarl
72
+ multiprocess==0.70.18
73
+ # via
74
+ # datasets
75
+ # evaluate
76
+ numpy==2.2.6
77
+ # via
78
+ # datasets
79
+ # evaluate
80
+ # pandas
81
+ packaging==25.0
82
+ # via
83
+ # datasets
84
+ # evaluate
85
+ # huggingface-hub
86
+ pandas==2.3.3
87
+ # via
88
+ # datasets
89
+ # evaluate
90
+ pillow==12.0.0
91
+ # via layout-utility
92
+ propcache==0.4.1
93
+ # via
94
+ # aiohttp
95
+ # yarl
96
+ pyarrow==22.0.0
97
+ # via datasets
98
+ python-dateutil==2.9.0.post0
99
+ # via pandas
100
+ pytz==2025.2
101
+ # via pandas
102
+ pyyaml==6.0.3
103
+ # via
104
+ # datasets
105
+ # huggingface-hub
106
+ requests==2.32.5
107
+ # via
108
+ # datasets
109
+ # evaluate
110
+ shellingham==1.5.4
111
+ # via huggingface-hub
112
+ six==1.17.0
113
+ # via python-dateutil
114
+ tqdm==4.67.1
115
+ # via
116
+ # datasets
117
+ # evaluate
118
+ # huggingface-hub
119
+ typer-slim==0.21.0
120
+ # via huggingface-hub
121
+ typing-extensions==4.15.0
122
+ # via
123
+ # aiosignal
124
+ # anyio
125
+ # huggingface-hub
126
+ # typer-slim
127
+ tzdata==2025.3
128
+ # via pandas
129
+ urllib3==2.6.2
130
+ # via requests
131
+ xxhash==3.6.0
132
+ # via
133
+ # datasets
134
+ # evaluate
135
+ yarl==1.22.0
136
+ # via aiohttp