shunk031 commited on
Commit
c34256f
·
1 Parent(s): fc14478

deploy: fb8f67e71123cce23e6f5de329f55e1af712771e

Browse files
Files changed (1) hide show
  1. layout-alignment.py +213 -0
layout-alignment.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Tuple, Union
2
+
3
+ import datasets as ds
4
+ import evaluate
5
+ import numpy as np
6
+ import numpy.typing as npt
7
+
8
+ _DESCRIPTION = """\
9
+ Computes some alignment metrics that are different to each other in previous works.
10
+ """
11
+
12
+ _KWARGS_DESCRIPTION = """\
13
+ Args:
14
+ bbox (`list` of `lists` of `int`): A list of lists of integers representing bounding boxes.
15
+ mask (`list` of `lists` of `bool`): A list of lists of booleans representing masks.
16
+
17
+ Returns:
18
+ dictionaly: A set of alignment scores.
19
+
20
+ Examples:
21
+
22
+ Example 1: Single processing
23
+ >>> metric = evaluate.load("pytorch-layout-generation/layout-alignment")
24
+ >>> model_max_length, num_coordinates = 25, 4
25
+ >>> bbox = np.random.rand(model_max_length, num_coordinates)
26
+ >>> mask = np.random.choice(a=[True, False], size=(model_max_length,))
27
+ >>> metric.add(bbox=bbox, mask=mask)
28
+ >>> print(metric.compute())
29
+
30
+ Example 2: Batch processing
31
+ >>> metric = evaluate.load("pytorch-layout-generation/layout-alignment")
32
+ >>> batch_size, model_max_length, num_coordinates = 512, 25, 4
33
+ >>> batch_bbox = np.random.rand(batch_size, model_max_length, num_coordinates)
34
+ >>> batch_mask = np.random.choice(a=[True, False], size=(batch_size, model_max_length))
35
+ >>> metric.add_batch(bbox=batch_bbox, mask=batch_mask)
36
+ >>> print(metric.compute())
37
+ """
38
+
39
+ _CITATION = """\
40
+ @inproceedings{lee2020neural,
41
+ title={Neural design network: Graphic layout generation with constraints},
42
+ author={Lee, Hsin-Ying and Jiang, Lu and Essa, Irfan and Le, Phuong B and Gong, Haifeng and Yang, Ming-Hsuan and Yang, Weilong},
43
+ booktitle={Computer Vision--ECCV 2020: 16th European Conference, Glasgow, UK, August 23--28, 2020, Proceedings, Part III 16},
44
+ pages={491--506},
45
+ year={2020},
46
+ organization={Springer}
47
+ }
48
+
49
+ @article{li2020attribute,
50
+ title={Attribute-conditioned layout gan for automatic graphic design},
51
+ author={Li, Jianan and Yang, Jimei and Zhang, Jianming and Liu, Chang and Wang, Christina and Xu, Tingfa},
52
+ journal={IEEE Transactions on Visualization and Computer Graphics},
53
+ volume={27},
54
+ number={10},
55
+ pages={4039--4048},
56
+ year={2020},
57
+ publisher={IEEE}
58
+ }
59
+
60
+ @inproceedings{kikuchi2021constrained,
61
+ title={Constrained graphic layout generation via latent optimization},
62
+ author={Kikuchi, Kotaro and Simo-Serra, Edgar and Otani, Mayu and Yamaguchi, Kota},
63
+ booktitle={Proceedings of the 29th ACM International Conference on Multimedia},
64
+ pages={88--96},
65
+ year={2021}
66
+ }
67
+ """
68
+
69
+
70
+ def convert_xywh_to_ltrb(
71
+ batch_bbox: npt.NDArray[np.float64],
72
+ ) -> Tuple[
73
+ npt.NDArray[np.float64],
74
+ npt.NDArray[np.float64],
75
+ npt.NDArray[np.float64],
76
+ npt.NDArray[np.float64],
77
+ ]:
78
+ xc, yc, w, h = batch_bbox
79
+ x1 = xc - w / 2
80
+ y1 = yc - h / 2
81
+ x2 = xc + w / 2
82
+ y2 = yc + h / 2
83
+ return (x1, y1, x2, y2)
84
+
85
+
86
+ class LayoutAlignment(evaluate.Metric):
87
+ def _info(self) -> evaluate.EvaluationModuleInfo:
88
+ return evaluate.MetricInfo(
89
+ description=_DESCRIPTION,
90
+ citation=_CITATION,
91
+ inputs_description=_KWARGS_DESCRIPTION,
92
+ features=ds.Features(
93
+ {
94
+ "bbox": ds.Sequence(ds.Sequence(ds.Value("float64"))),
95
+ "mask": ds.Sequence(ds.Value("bool")),
96
+ }
97
+ ),
98
+ codebase_urls=[
99
+ "https://github.com/ktrk115/const_layout/blob/master/metric.py#L167-L188",
100
+ "https://github.com/CyberAgentAILab/layout-dm/blob/main/src/trainer/trainer/helpers/metric.py#L98-L147",
101
+ ],
102
+ )
103
+
104
+ def _compute_ac_layout_gan(
105
+ self,
106
+ S: int,
107
+ xl: npt.NDArray[np.float64],
108
+ xc: npt.NDArray[np.float64],
109
+ xr: npt.NDArray[np.float64],
110
+ yt: npt.NDArray[np.float64],
111
+ yc: npt.NDArray[np.float64],
112
+ yb: npt.NDArray[np.float64],
113
+ batch_mask: npt.NDArray,
114
+ ) -> npt.NDArray[np.float64]:
115
+ # shape: (B, 6, S)
116
+ X = np.stack((xl, xc, xr, yt, yc, yb), axis=1)
117
+ # shape: (B, 6, S, 1) - (B, 6, 1, S) = (B, 6 S, S)
118
+ X = X[:, :, :, None] - X[:, :, None, :]
119
+
120
+ # shape: (S,)
121
+ indices = np.arange(S)
122
+ X[:, :, indices, indices] = 1.0
123
+ # shape: (B, 6, S, S -> (B, S, 6, S)
124
+ X = np.abs(X).transpose(0, 2, 1, 3)
125
+ X[~batch_mask] = 1.0
126
+
127
+ # shape: (B, S, 6, S) -> (B, S)
128
+ X = X.min(axis=(2, 3))
129
+ X[X == 1.0] = 0.0
130
+ X = -np.log(1 - X)
131
+
132
+ # shape: (B, S) -> (B,)
133
+ return X.sum(axis=1)
134
+
135
+ def _compute_layout_gan_pp(
136
+ self,
137
+ score_ac_layout_gan: npt.NDArray[np.float64],
138
+ batch_mask: npt.NDArray[np.bool_],
139
+ ) -> npt.NDArray[np.float64]:
140
+ # shape: (B, S) -> (B,)
141
+ batch_mask = batch_mask.sum(axis=1)
142
+
143
+ # shape: (B,)
144
+ score_normalized = score_ac_layout_gan / batch_mask
145
+ score_normalized[np.isnan(score_normalized)] = 0.0
146
+ return score_normalized
147
+
148
+ def _compute_neural_design_network(
149
+ self,
150
+ xl: npt.NDArray[np.float64],
151
+ xc: npt.NDArray[np.float64],
152
+ xr: npt.NDArray[np.float64],
153
+ batch_mask: npt.NDArray[np.bool_],
154
+ S: int,
155
+ ):
156
+ # shape: (B, 3, S)
157
+ Y = np.stack((xl, xc, xr), axis=1)
158
+ # shape: (B, 3, S, S)
159
+ Y = Y[:, :, None, :] - Y[:, :, :, None]
160
+
161
+ # shape: (B, S) -> (B, S, S)
162
+ batch_mask = ~batch_mask[:, None, :] | ~batch_mask[:, :, None]
163
+ # shape: (B,)
164
+ indices = np.arange(S)
165
+ batch_mask[:, indices, indices] = True
166
+
167
+ # shape: (B, S, S) -> (B, 1, S, S) -> (B, 3, S, S)
168
+ batch_mask = np.repeat(batch_mask[:, None, :, :], repeats=3, axis=1)
169
+ Y[batch_mask] = 1.0
170
+
171
+ # shape: (B, 3, S, S) -> (B, S, S) -> (B, S)
172
+ Y = np.abs(Y).min(axis=(1, 2))
173
+ Y[Y == 1.0] = 0.0
174
+
175
+ # shape: (B, S) -> (B,)
176
+ score = Y.sum(axis=1)
177
+ return score
178
+
179
+ def _compute(
180
+ self,
181
+ *,
182
+ bbox: Union[npt.NDArray[np.float64], List[List[int]]],
183
+ mask: Union[npt.NDArray[np.bool_], List[List[bool]]],
184
+ ) -> Dict[str, npt.NDArray[np.float64]]:
185
+ # shape: (B, model_max_length, C)
186
+ bbox = np.array(bbox)
187
+ # shape: (B, model_max_length)
188
+ mask = np.array(mask)
189
+
190
+ # S: model_max_length
191
+ _, S, _ = bbox.shape
192
+
193
+ # shape: (B, S, C) -> (C, B, S)
194
+ bbox = bbox.transpose(2, 0, 1)
195
+ xl, yt, xr, yb = convert_xywh_to_ltrb(bbox)
196
+ xc, yc = bbox[0], bbox[1]
197
+
198
+ # shape: (B,)
199
+ score_ac_layout_gan = self._compute_ac_layout_gan(
200
+ S=S, xl=xl, xc=xc, xr=xr, yt=yt, yc=yc, yb=yb, batch_mask=mask
201
+ )
202
+ # shape: (B,)
203
+ score_layout_gan_pp = self._compute_layout_gan_pp(
204
+ score_ac_layout_gan=score_ac_layout_gan, batch_mask=mask
205
+ )
206
+ score_ndn = self._compute_neural_design_network(
207
+ xl=xl, xc=xc, xr=xr, batch_mask=mask, S=S
208
+ )
209
+ return {
210
+ "alignment-ACLayoutGAN": score_ac_layout_gan,
211
+ "alignment-LayoutGAN++": score_layout_gan_pp,
212
+ "alignment-NDN": score_ndn,
213
+ }