dikdimon commited on
Commit
dddbb4b
·
verified ·
1 Parent(s): 21bc3b8

Upload sd-webui-convenience-util using SD-Hub

Browse files
sd-webui-convenience-util/.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
sd-webui-convenience-util/scripts/__pycache__/build_sdxl_catalog.cpython-310.pyc ADDED
Binary file (20.5 kB). View file
 
sd-webui-convenience-util/scripts/__pycache__/conv_ui.cpython-310.pyc ADDED
Binary file (18.6 kB). View file
 
sd-webui-convenience-util/scripts/build_sdxl_catalog.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ SDXL Resolution Catalog Builder (with short-side packages & favorites/tags)
5
+ Python 3.10+ (stdlib only)
6
+
7
+ Назначение:
8
+ • Генерирует/расширяет каталог разрешений (SDXL/SD) с категориями:
9
+ - по аспектам (и портретные зеркала),
10
+ - по диапазонам мегапикселей (MP bands),
11
+ - по ориентации (landscape/portrait/square),
12
+ - по короткой стороне через именованные пакеты,
13
+ - с тегами/избранным.
14
+ • Экспортирует:
15
+ - sdxl_resolutions_by_aspect.txt (читаемо + Flat list WxH),
16
+ - sdxl_resolutions_catalog.csv,
17
+ - sdxl_resolutions_catalog.json,
18
+ - sdxl_resolutions_catalog.yaml (простой YAML без внешних пакетов).
19
+
20
+ Почему без dataclasses:
21
+ В окружениях A1111 модульные загрузчики иногда импортируют скрипт так,
22
+ что в момент декоратора @dataclass модуль ещё не в sys.modules — это
23
+ ломает внутреннюю логику dataclasses в Py3.10. Мы избегаем этого, используя
24
+ обычный класс с __slots__.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import argparse
30
+ import datetime as dt
31
+ import json
32
+ import os
33
+ import re
34
+ import csv
35
+ from math import gcd
36
+ from typing import Iterable, List, Tuple, Dict, Optional
37
+
38
+
39
+ # ──────────────────────────────────────────────────────────────────────────────
40
+ # Модель данных (без dataclasses: совместимо с импортом в A1111)
41
+ # ──────────────────────────────────────────────────────────────────────────────
42
+
43
+ class ResItem:
44
+ __slots__ = ("width", "height", "mp", "aspect", "orientation", "packages", "tags", "bucket_aspect")
45
+
46
+ def __init__(self,
47
+ width: int,
48
+ height: int,
49
+ mp: float,
50
+ aspect: str,
51
+ orientation: str,
52
+ packages: Optional[List[str]] = None,
53
+ tags: Optional[List[str]] = None,
54
+ bucket_aspect: Optional[str] = None):
55
+ self.width = int(width)
56
+ self.height = int(height)
57
+ self.mp = float(mp)
58
+ self.aspect = aspect # фактическая кратная дробь, например "1024:1536" → "2:3"
59
+ self.orientation = orientation
60
+ self.packages = list(packages or [])
61
+ self.tags = list(tags or [])
62
+ self.bucket_aspect = bucket_aspect or aspect # целевой "ключ" секции (напр. "3:2"), если попали по tolerance
63
+
64
+ def to_dict(self) -> Dict:
65
+ return {
66
+ "width": self.width,
67
+ "height": self.height,
68
+ "mp": round(self.mp, 3),
69
+ "aspect": self.aspect,
70
+ "orientation": self.orientation,
71
+ "packages": list(self.packages),
72
+ "tags": list(self.tags),
73
+ "bucket_aspect": self.bucket_aspect,
74
+ }
75
+
76
+
77
+ # ──────────────────────────────────────────────────────────────────────────────
78
+ # Утилиты
79
+ # ──────────────────────────────────────────────────────────────────────────────
80
+
81
+ def simplified_ratio(width: int, height: int) -> str:
82
+ if width <= 0 or height <= 0:
83
+ return "—"
84
+ g = gcd(width, height)
85
+ return f"{width // g}:{height // g}"
86
+
87
+ def orientation_of(w: int, h: int) -> str:
88
+ if w == h:
89
+ return "square"
90
+ return "landscape" if w > h else "portrait"
91
+
92
+ def megapixels(w: int, h: int) -> float:
93
+ return round((w * h) / 1_000_000.0, 3)
94
+
95
+ def parse_wxh_pairs(text: str) -> List[Tuple[int, int]]:
96
+ """Достаёт все пары WxH (3-4 цифры) из произвольного текста; порядок сохраняем, дубли убираем."""
97
+ dims: List[Tuple[int, int]] = []
98
+ seen: set[Tuple[int, int]] = set()
99
+ for w, h in re.findall(r"(\d{3,4})x(\d{3,4})", text):
100
+ pair = (int(w), int(h))
101
+ if pair not in seen:
102
+ seen.add(pair)
103
+ dims.append(pair)
104
+ return dims
105
+
106
+ def load_existing_dims(path: Optional[str]) -> List[Tuple[int, int]]:
107
+ if not path or not os.path.isfile(path):
108
+ return []
109
+ try:
110
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
111
+ return parse_wxh_pairs(f.read())
112
+ except Exception:
113
+ return []
114
+
115
+ def nearly_equal_ratio(w: int, h: int, ax: int, ay: int, tol: float) -> bool:
116
+ """Проверка близости w:h к ax:ay с допуском tol (например, 0.02 ~ 2%)."""
117
+ if w <= 0 or h <= 0 or ax <= 0 or ay <= 0:
118
+ return False
119
+ return abs((w / h) - (ax / ay)) <= tol
120
+
121
+ def generate_grid(min_size: int, max_size: int, step: int) -> Iterable[Tuple[int, int]]:
122
+ sizes = list(range(min_size, max_size + 1, step))
123
+ for w in sizes:
124
+ for h in sizes:
125
+ yield w, h
126
+
127
+
128
+ # ──────────────────────────────────────────────────────────────────────────────
129
+ # Парсеры CLI-аргументов (пакеты/теги)
130
+ # ──────────────────────────────────────────────────────────────────────────────
131
+
132
+ def parse_shortside_packs(s: str) -> Dict[str, List[int]]:
133
+ """
134
+ "name1:512,768,1024; name2:1152,1216,1344" -> {"name1":[512,768,1024], "name2":[...]}
135
+ """
136
+ packs: Dict[str, List[int]] = {}
137
+ if not s.strip():
138
+ return packs
139
+ for block in re.split(r"[;]+", s):
140
+ block = block.strip()
141
+ if not block:
142
+ continue
143
+ m = re.match(r"([^:]+):(.+)$", block)
144
+ if not m:
145
+ raise ValueError(f"Bad shortside pack: {block!r}. Expected 'name:val,val,...'")
146
+ name = m.group(1).strip()
147
+ vals = [int(x) for x in re.split(r"[,\s]+", m.group(2).strip()) if x.isdigit()]
148
+ if not vals:
149
+ raise ValueError(f"Empty values for pack {name!r}")
150
+ packs[name] = sorted(set(vals))
151
+ return packs
152
+
153
+ def parse_favorites_inline(s: str) -> Dict[Tuple[int, int], List[str]]:
154
+ """
155
+ "1024x1536|fav,hero; 1152x1152|square; 1536x1024" ->
156
+ {(1024,1536): ['fav','hero'], (1152,1152): ['square'], (1536,1024): ['favorite']}
157
+ """
158
+ out: Dict[Tuple[int, int], List[str]] = {}
159
+ if not s.strip():
160
+ return out
161
+ for block in re.split(r"[;]+", s):
162
+ block = block.strip()
163
+ if not block:
164
+ continue
165
+ m = re.match(r"(\d{3,4})x(\d{3,4})(?:\s*\|\s*(.+))?$", block)
166
+ if not m:
167
+ raise ValueError(f"Bad favorites item: {block!r}. Expected 'WxH|tag1,tag2' or 'WxH'")
168
+ w, h = int(m.group(1)), int(m.group(2))
169
+ tags = []
170
+ if m.group(3):
171
+ tags = [t.strip() for t in re.split(r"[,\s]+", m.group(3)) if t.strip()]
172
+ if not tags:
173
+ tags = ["favorite"]
174
+ out[(w, h)] = sorted(set(tags))
175
+ return out
176
+
177
+ def parse_favorites_file(path: Optional[str]) -> Dict[Tuple[int, int], List[str]]:
178
+ """
179
+ Файл, по строкам:
180
+ 1024x1536 | fav,hero
181
+ 1152x1152 | square
182
+ 1536x1024
183
+ """
184
+ out: Dict[Tuple[int, int], List[str]] = {}
185
+ if not path or not os.path.isfile(path):
186
+ return out
187
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
188
+ for line in f:
189
+ line = line.strip()
190
+ if not line or line.startswith("#"):
191
+ continue
192
+ m = re.match(r"(\d{3,4})x(\d{3,4})(?:\s*\|\s*(.+))?$", line)
193
+ if not m:
194
+ mm = re.search(r"(\d{3,4})x(\d{3,4})", line)
195
+ if not mm:
196
+ continue
197
+ w, h = int(mm.group(1)), int(mm.group(2))
198
+ tags = ["favorite"]
199
+ else:
200
+ w, h = int(m.group(1)), int(m.group(2))
201
+ tags = []
202
+ if m.group(3):
203
+ tags = [t.strip() for t in re.split(r"[,\s]+", m.group(3)) if t.strip()]
204
+ if not tags:
205
+ tags = ["favorite"]
206
+ out[(w, h)] = sorted(set(tags))
207
+ return out
208
+
209
+
210
+ # ──────────────────────────────────────────────────────────────────────────────
211
+ # Генерация/фильтрация каталога
212
+ # ──────────────────────────────────────────────────────────────────────────────
213
+
214
+ def build_catalog(
215
+ min_size: int,
216
+ max_size: int,
217
+ step: int,
218
+ multiple: int,
219
+ aspects: List[Tuple[int, int]],
220
+ tolerance: float,
221
+ merge_extra: List[Tuple[int, int]] | None = None,
222
+ min_mp: float = 0.0,
223
+ max_mp: float = 999.0,
224
+ shortside_packs: Dict[str, List[int]] | None = None,
225
+ favorites: Dict[Tuple[int, int], List[str]] | None = None,
226
+ ) -> List[ResItem]:
227
+ """
228
+ Генерирует сетку размеров, фильтрует по кратности/аспектам, размечает пакеты и теги.
229
+ ВАЖНО: пары, попавшие к аспекту по tolerance, в отчёте группируются в секции этого аспекта.
230
+ """
231
+ out: List[ResItem] = []
232
+ seen: set[Tuple[int, int]] = set()
233
+ packs = shortside_packs or {}
234
+ favs = favorites or {}
235
+
236
+ def packages_for(w: int, h: int) -> List[str]:
237
+ short = min(w, h)
238
+ res: List[str] = []
239
+ for name, values in packs.items():
240
+ if short in values:
241
+ res.append(name)
242
+ return sorted(set(res))
243
+
244
+ def tags_for(w: int, h: int) -> List[str]:
245
+ return favs.get((w, h), [])
246
+
247
+ def add_pair(w: int, h: int, bucket: str):
248
+ key = (w, h)
249
+ if key in seen:
250
+ return
251
+ mp = megapixels(w, h)
252
+ if mp < min_mp or mp > max_mp:
253
+ return
254
+ item = ResItem(
255
+ width=w,
256
+ height=h,
257
+ mp=mp,
258
+ aspect=simplified_ratio(w, h),
259
+ orientation=orientation_of(w, h),
260
+ packages=packages_for(w, h),
261
+ tags=tags_for(w, h),
262
+ bucket_aspect=bucket,
263
+ )
264
+ out.append(item)
265
+ seen.add(key)
266
+
267
+ # 1) Основная решётка
268
+ for w, h in generate_grid(min_size, max_size, step):
269
+ if multiple > 1 and (w % multiple != 0 or h % multiple != 0):
270
+ continue
271
+ # найдём первый подходящий аспект (точный или с допуском)
272
+ bucket: Optional[str] = None
273
+ for ax, ay in aspects:
274
+ if (w * ay) == (h * ax): # точная кратность
275
+ bucket = f"{ax}:{ay}"
276
+ break
277
+ if nearly_equal_ratio(w, h, ax, ay, tolerance) or nearly_equal_ratio(w, h, ay, ax, tolerance):
278
+ bucket = f"{ax}:{ay}"
279
+ break
280
+ if not bucket:
281
+ continue
282
+ add_pair(w, h, bucket)
283
+
284
+ # 2) Слияние с существующим .txt
285
+ for w, h in merge_extra or []:
286
+ if multiple > 1 and (w % multiple != 0 or h % multiple != 0):
287
+ continue
288
+ bucket: Optional[str] = None
289
+ for ax, ay in aspects:
290
+ if (w * ay) == (h * ax) or nearly_equal_ratio(w, h, ax, ay, tolerance) or nearly_equal_ratio(w, h, ay, ax, tolerance):
291
+ bucket = f"{ax}:{ay}"
292
+ break
293
+ if not bucket:
294
+ continue
295
+ add_pair(w, h, bucket)
296
+
297
+ # Стабильная сортировка: по MP → ширине → высоте
298
+ out.sort(key=lambda r: (r.mp, r.width, r.height))
299
+ return out
300
+
301
+
302
+ # ──────────────────────────────────────────────────────────────────────────────
303
+ # Экспорт
304
+ # ──────────────────────────────────────────────────────────────────────────────
305
+
306
+ def dump_txt_structured(
307
+ items: List[ResItem],
308
+ aspects: List[Tuple[int, int]],
309
+ mp_bands: List[Tuple[float, float]],
310
+ out_path: str,
311
+ header_title: str,
312
+ shortside_packs: Dict[str, List[int]],
313
+ ) -> None:
314
+ """
315
+ Структурированный .txt:
316
+ • Сводка
317
+ • Секции по аспектам → MP-бэндам → ориентациям (помечаем ★[tags] и {packages})
318
+ • Секция «Short-side packages»
319
+ • В конце — Flat list (только WxH)
320
+ """
321
+ # Группировка по "целевым" аспектам (ax:ay). Зеркала не создают новых ключей.
322
+ by_aspect: Dict[str, List[ResItem]] = {f"{ax}:{ay}": [] for (ax, ay) in aspects}
323
+ for it in items:
324
+ key = it.bucket_aspect
325
+ if key not in by_aspect:
326
+ by_aspect[key] = []
327
+ by_aspect[key].append(it)
328
+
329
+ def aspect_value(s: str) -> float:
330
+ try:
331
+ a, b = s.split(":")
332
+ return int(a) / int(b)
333
+ except Exception:
334
+ return 0.0
335
+
336
+ ordered_aspects = sorted(by_aspect.keys(), key=aspect_value)
337
+
338
+ def band_name(lo: float, hi: float) -> str:
339
+ return f"{lo:.2g}MP+" if hi >= 999 else f"{lo:.2g}–{hi:.2g}MP"
340
+
341
+ def mirror_label(s: str) -> str:
342
+ parts = s.split(":")
343
+ if len(parts) == 2:
344
+ return f"{parts[1]}:{parts[0]}"
345
+ return s
346
+
347
+ now = dt.datetime.now().strftime("%Y-%m-%d %H:%M")
348
+
349
+ with open(out_path, "w", encoding="utf-8") as f:
350
+ f.write(f"# {header_title}\n")
351
+ f.write(f"# Generated: {now}\n")
352
+ f.write("# File format: human-friendly sections + raw WxH pairs (backward-compatible)\n\n")
353
+
354
+ # Summary
355
+ total = len(items)
356
+ fav_total = sum(1 for it in items if it.tags)
357
+ f.write("## Summary\n")
358
+ f.write(f"- Total items: {total}\n")
359
+ f.write(f"- Favorites / tagged: {fav_total}\n")
360
+ f.write(f"- Aspects covered: {', '.join(ordered_aspects)}\n")
361
+ f.write(f"- MP bands: {', '.join([band_name(lo, hi) for lo, hi in mp_bands])}\n")
362
+ if shortside_packs:
363
+ f.write(f"- Short-side packs: {', '.join(f'{n}({len(v)})' for n, v in shortside_packs.items())}\n")
364
+ f.write("\n---\n\n")
365
+
366
+ # Секции по аспектам
367
+ for asp_key in ordered_aspects:
368
+ bucket = by_aspect[asp_key]
369
+ if not bucket:
370
+ continue
371
+ f.write(f"## Aspect {asp_key} / {mirror_label(asp_key)}\n\n")
372
+ for (lo, hi) in mp_bands:
373
+ band_items = [it for it in bucket if (it.mp >= lo and it.mp <= hi)]
374
+ if not band_items:
375
+ continue
376
+ f.write(f"### MP band: {band_name(lo, hi)}\n\n")
377
+ for ori in ("landscape", "portrait", "square"):
378
+ sub = [it for it in band_items if it.orientation == ori]
379
+ if not sub:
380
+ continue
381
+ f.write(f"**{ori}**\n")
382
+ line: List[str] = []
383
+ for it in sub:
384
+ label = f"{it.width}x{it.height}"
385
+ if it.tags:
386
+ label += " ★"
387
+ label += f"[{','.join(it.tags)}]"
388
+ if it.packages:
389
+ label += f" {{{','.join(it.packages)}}}"
390
+ line.append(label)
391
+ if len(line) >= 6:
392
+ f.write("- " + ", ".join(line) + "\n")
393
+ line = []
394
+ if line:
395
+ f.write("- " + ", ".join(line) + "\n")
396
+ f.write("\n")
397
+ f.write("\n")
398
+
399
+ # Пакеты по короткой стороне
400
+ if shortside_packs:
401
+ f.write("\n---\n\n")
402
+ f.write("## Short-side packages\n\n")
403
+ for name, values in shortside_packs.items():
404
+ f.write(f"### {name}\n")
405
+ pack_items = [it for it in items if name in it.packages]
406
+ if not pack_items:
407
+ f.write("- (empty)\n\n")
408
+ continue
409
+ # Группировка: short_side -> [items]
410
+ ss_map: Dict[int, List[ResItem]] = {}
411
+ for it in pack_items:
412
+ short = min(it.width, it.height)
413
+ ss_map.setdefault(short, []).append(it)
414
+ for short in sorted(ss_map.keys()):
415
+ f.write(f"**short={short}**\n")
416
+ group = sorted(ss_map[short], key=lambda r: (r.mp, r.orientation, r.width, r.height))
417
+ line: List[str] = []
418
+ for it in group:
419
+ label = f"{it.width}x{it.height}"
420
+ if it.tags:
421
+ label += " ★"
422
+ label += f"[{','.join(it.tags)}]"
423
+ line.append(label)
424
+ if len(line) >= 8:
425
+ f.write("- " + ", ".join(line) + "\n")
426
+ line = []
427
+ if line:
428
+ f.write("- " + ", ".join(line) + "\n")
429
+ f.write("\n")
430
+
431
+ # Плоский список (для старых парсеров)
432
+ f.write("\n---\n")
433
+ f.write("# Flat list (WxH only)\n")
434
+ for it in items:
435
+ f.write(f"{it.width}x{it.height}\n")
436
+
437
+ def dump_csv(items: List[ResItem], out_path: str) -> None:
438
+ with open(out_path, "w", newline="", encoding="utf-8") as f:
439
+ w = csv.writer(f)
440
+ w.writerow(["width", "height", "mp", "aspect", "orientation", "packages", "tags", "bucket_aspect"])
441
+ for it in items:
442
+ w.writerow([
443
+ it.width, it.height, round(it.mp, 3), it.aspect, it.orientation,
444
+ ";".join(it.packages), ";".join(it.tags), it.bucket_aspect
445
+ ])
446
+
447
+ def dump_json(items: List[ResItem], out_path: str) -> None:
448
+ data = [it.to_dict() for it in items]
449
+ with open(out_path, "w", encoding="utf-8") as f:
450
+ json.dump(data, f, ensure_ascii=False, indent=2)
451
+
452
+ def dump_yaml(items: List[ResItem], out_path: str) -> None:
453
+ """Простой YAML без внешних либ."""
454
+ def esc(s: str) -> str:
455
+ return s.replace('\\', '\\\\').replace('"', '\\"')
456
+ with open(out_path, "w", encoding="utf-8") as f:
457
+ f.write("items:\n")
458
+ for it in items:
459
+ d = it.to_dict()
460
+ packs = ", ".join([f"\"{esc(p)}\"" for p in d["packages"]])
461
+ tags = ", ".join([f"\"{esc(t)}\"" for t in d["tags"]])
462
+ f.write(" - {\n")
463
+ f.write(f" width: {d['width']},\n")
464
+ f.write(f" height: {d['height']},\n")
465
+ f.write(f" mp: {round(d['mp'], 3)},\n")
466
+ f.write(f" aspect: \"{esc(d['aspect'])}\",\n")
467
+ f.write(f" orientation: \"{esc(d['orientation'])}\",\n")
468
+ f.write(f" packages: [{packs}],\n")
469
+ f.write(f" tags: [{tags}],\n")
470
+ f.write(f" bucket_aspect: \"{esc(d['bucket_aspect'])}\"\n")
471
+ f.write(" }\n")
472
+
473
+
474
+ # ──────────────────────────────────────────────────────────────────────────────
475
+ # CLI
476
+ # ──────────────────────────────────────────────────────────────────────────────
477
+
478
+ def parse_aspects(s: str) -> List[Tuple[int, int]]:
479
+ out: List[Tuple[int, int]] = []
480
+ for tok in re.split(r"[;,]+", s):
481
+ tok = tok.strip()
482
+ if not tok:
483
+ continue
484
+ m = re.match(r"(\d+)\s*:\s*(\d+)$", tok)
485
+ if not m:
486
+ raise ValueError(f"Bad aspect token: {tok!r}")
487
+ out.append((int(m.group(1)), int(m.group(2))))
488
+ if not out:
489
+ raise ValueError("No aspects provided.")
490
+ return out
491
+
492
+ def parse_bands(s: str) -> List[Tuple[float, float]]:
493
+ out: List[Tuple[float, float]] = []
494
+ for tok in re.split(r"[;,]+", s):
495
+ tok = tok.strip()
496
+ if not tok:
497
+ continue
498
+ m = re.match(r"(\d+(?:\.\d+)?)\s*-\s*(\d+(?:\.\d+)?)$", tok)
499
+ if not m:
500
+ raise ValueError(f"Bad mp band: {tok!r}, expected like '1-2'")
501
+ lo, hi = float(m.group(1)), float(m.group(2))
502
+ if lo > hi:
503
+ lo, hi = hi, lo
504
+ out.append((lo, hi))
505
+ if not out:
506
+ raise ValueError("No mp bands provided.")
507
+ return out
508
+
509
+ def main():
510
+ ap = argparse.ArgumentParser(description="Build expanded SDXL resolution catalog.")
511
+ ap.add_argument("--min", dest="min_size", type=int, default=512, help="Minimum side, px")
512
+ ap.add_argument("--max", dest="max_size", type=int, default=4096, help="Maximum side, px")
513
+ ap.add_argument("--step", type=int, default=64, help="Grid step, px")
514
+ ap.add_argument("--multiple", type=int, default=64, help="Required multiple for both sides, px")
515
+ ap.add_argument("--tolerance", type=float, default=0.02, help="Aspect tolerance (e.g., 0.02 ~ 2%)")
516
+ ap.add_argument("--aspects", type=str,
517
+ default="1:1,3:2,2:3,4:3,3:4,5:4,4:5,7:4,4:7,16:9,9:16,21:9,9:21",
518
+ help="Comma-separated list like '1:1,16:9,9:16'")
519
+ ap.add_argument("--mp-bands", type=str, default="0-1,1-2,2-3,3-4,4-6,6-8",
520
+ help="Comma-separated MP bands, e.g. '0-1,1-2,2-3'")
521
+ ap.add_argument("--merge", type=str, default="", help="Optional path to existing .txt with WxH to merge")
522
+ ap.add_argument("--out-dir", type=str, default=".", help="Output directory")
523
+
524
+ # Новые аргументы:
525
+ ap.add_argument("--shortside-packs", type=str, default="",
526
+ help="Short-side packages: 'name:512,768; name2:1024,1152'")
527
+ ap.add_argument("--favorites", type=str, default="",
528
+ help="Inline favorites/tags: '1024x1536|fav,hero; 1152x1152|square; 1536x1024'")
529
+ ap.add_argument("--favorites-file", type=str, default="",
530
+ help="File with favorites (WxH | tags) per line")
531
+
532
+ args = ap.parse_args()
533
+
534
+ # Валидации/приведения
535
+ min_size = max(128, int(args.min_size))
536
+ max_size = min(8192, max(min_size, int(args.max_size)))
537
+ step = max(8, int(args.step))
538
+ multiple = max(1, int(args.multiple))
539
+ tol = max(0.0, float(args.tolerance))
540
+ aspects = parse_aspects(args.aspects)
541
+ bands = parse_bands(args.mp_bands)
542
+ merge_dims = load_existing_dims(args.merge)
543
+
544
+ shortside_packs = parse_shortside_packs(args.shortside_packs)
545
+ favorites_inline = parse_favorites_inline(args.favorites)
546
+ favorites_file = parse_favorites_file(args.favorites_file)
547
+ favorites: Dict[Tuple[int, int], List[str]] = dict(favorites_file)
548
+ favorites.update(favorites_inline) # inline приоритетнее
549
+
550
+ # Сборка каталога
551
+ items = build_catalog(
552
+ min_size=min_size,
553
+ max_size=max_size,
554
+ step=step,
555
+ multiple=multiple,
556
+ aspects=aspects,
557
+ tolerance=tol,
558
+ merge_extra=merge_dims,
559
+ min_mp=min(b[0] for b in bands) if bands else 0.0,
560
+ max_mp=max(b[1] for b in bands) if bands else 999.0,
561
+ shortside_packs=shortside_packs,
562
+ favorites=favorites,
563
+ )
564
+
565
+ # Пути
566
+ os.makedirs(args.out_dir, exist_ok=True)
567
+ out_txt = os.path.join(args.out_dir, "sdxl_resolutions_by_aspect.txt")
568
+ out_csv = os.path.join(args.out_dir, "sdxl_resolutions_catalog.csv")
569
+ out_json = os.path.join(args.out_dir, "sdxl_resolutions_catalog.json")
570
+ out_yaml = os.path.join(args.out_dir, "sdxl_resolutions_catalog.yaml")
571
+
572
+ # Заголовок
573
+ header_title = (
574
+ f"SDXL Resolution Catalog — range {min_size}..{max_size} step={step} "
575
+ f"multiple={multiple} tol={tol} (aspects: {', '.join([f'{a}:{b}' for a,b in aspects])})"
576
+ )
577
+
578
+ # Экспорт
579
+ dump_txt_structured(items, aspects, bands, out_txt, header_title, shortside_packs)
580
+ dump_csv(items, out_csv)
581
+ dump_json(items, out_json)
582
+ dump_yaml(items, out_yaml)
583
+
584
+ # Короткий отчёт
585
+ print(f"[OK] Items: {len(items)}")
586
+ print(f"[OK] Wrote: {out_txt}")
587
+ print(f"[OK] Wrote: {out_csv}")
588
+ print(f"[OK] Wrote: {out_json}")
589
+ print(f"[OK] Wrote: {out_yaml}")
590
+
591
+
592
+ if __name__ == "__main__":
593
+ main()
sd-webui-convenience-util/scripts/conv_ui.py ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Image Ratio & Resolution Catalog Pro — enhanced
2
+ # Python 3.10+; gradio >= 3.32; pillow >= 9.0
3
+
4
+ from __future__ import annotations
5
+
6
+ import math
7
+ import re
8
+ import os
9
+ import io
10
+ from math import gcd, lcm
11
+ from typing import List, Tuple, Literal, Dict, Optional
12
+
13
+ import gradio as gr
14
+ from PIL import Image
15
+
16
+ # ──────────────────────────────────────────────────────────────────────────────
17
+ # Базовые утилиты
18
+ # ──────────────────────────────────────────────────────────────────────────────
19
+
20
+ RoundingMode = Literal["round", "floor", "ceil"]
21
+ ModelFamily = Literal["SDXL (native~1024)", "SD 1.x (native~512)", "SD 2.x (native~768)"]
22
+
23
+ DEFAULT_MODEL_FAMILY: ModelFamily = "SDXL (native~1024)"
24
+
25
+ def ensure_multiple(value: int, multiple: int, mode: RoundingMode = "round") -> int:
26
+ if multiple <= 1:
27
+ return max(1, value)
28
+ q, r = divmod(value, multiple)
29
+ if r == 0:
30
+ return max(multiple, value)
31
+ if mode == "floor":
32
+ return max(multiple, q * multiple)
33
+ if mode == "ceil":
34
+ return (q + 1) * multiple
35
+ lower = q * multiple
36
+ upper = (q + 1) * multiple
37
+ return upper if (value - lower) >= (upper - value) else lower
38
+
39
+ def simplified_ratio(width: int, height: int) -> str:
40
+ if width <= 0 or height <= 0:
41
+ return "—"
42
+ g = gcd(width, height)
43
+ return f"{width // g}:{height // g}"
44
+
45
+ def megapixels(width: int, height: int) -> float:
46
+ return round((width * height) / 1_000_000.0, 3)
47
+
48
+ def parse_dims(text: str) -> List[Tuple[int, int]]:
49
+ """Вытягивает все пары 'WxH' из произвольного текста."""
50
+ dims: List[Tuple[int, int]] = []
51
+ for w, h in re.findall(r"(\d{3,4})x(\d{3,4})", text):
52
+ dims.append((int(w), int(h)))
53
+ # удалим дубликаты, сохраняя порядок
54
+ seen = set()
55
+ uniq: List[Tuple[int, int]] = []
56
+ for d in dims:
57
+ if d not in seen:
58
+ seen.add(d)
59
+ uniq.append(d)
60
+ return uniq
61
+
62
+ # ──────────────────────────────────────────────────────────────────────────────
63
+ # Автоподбор кратности (multiple)
64
+ # ──────────────────────────────────────────────────────────────────────────────
65
+
66
+ def pick_base_multiple_by_model(model_family: ModelFamily, target_max_side: Optional[int] = None) -> int:
67
+ """
68
+ Эвристика:
69
+ - SDXL: кратность 64 (родной 1024, датасеты шагом 64).
70
+ - SD 2.x: минимум 16..32, чаще 32 в HD.
71
+ - SD 1.x: минимум 8..16, чаще 32 в HD.
72
+ """
73
+ if model_family == "SDXL (native~1024)":
74
+ base = 64
75
+ elif model_family == "SD 2.x (native~768)":
76
+ base = 32
77
+ else: # SD 1.x
78
+ base = 32
79
+ if target_max_side and target_max_side >= 1536:
80
+ base = max(base, 64)
81
+ return base
82
+
83
+ def combine_multiples(base_multiple: int, diffusion_tile: int = 0, vae_tile: int = 0) -> int:
84
+ """
85
+ Объединяем ограничения: НОК(base_multiple, diffusion_tile, vae_tile).
86
+ Нули игнорируются. Жёстко ограничим верх до 256.
87
+ """
88
+ m = base_multiple
89
+ for x in (diffusion_tile, vae_tile):
90
+ if isinstance(x, int) and x >= 8:
91
+ m = lcm(m, x)
92
+ return max(8, min(m, 256))
93
+
94
+ def explain_auto_multiple(model_family: ModelFamily, max_side: Optional[int], m_base: int, m_final: int,
95
+ diffusion_tile: int, vae_tile: int) -> str:
96
+ reasons = [f"модель: {model_family} -> base={m_base}"]
97
+ if max_side:
98
+ reasons.append(f"размер цели: ~{max_side}px")
99
+ if diffusion_tile:
100
+ reasons.append(f"diffusion tile={diffusion_tile}")
101
+ if vae_tile:
102
+ reasons.append(f"VAE tile={vae_tile}")
103
+ if m_final != m_base:
104
+ reasons.append(f"НОК -> {m_final}")
105
+ return " | ".join(reasons)
106
+
107
+ def autopick_multiple(model_family: ModelFamily,
108
+ diffusion_tile: int,
109
+ vae_tile: int,
110
+ width_hint: Optional[int],
111
+ height_hint: Optional[int]) -> Tuple[int, str]:
112
+ max_side = None
113
+ if width_hint and height_hint:
114
+ max_side = max(width_hint, height_hint)
115
+ m_base = pick_base_multiple_by_model(model_family, max_side)
116
+ m_final = combine_multiples(m_base, diffusion_tile, vae_tile)
117
+ return m_final, explain_auto_multiple(model_family, max_side, m_base, m_final, diffusion_tile, vae_tile)
118
+
119
+ # ──────────────────────────────────────────────────────────────────────────────
120
+ # Ресайзы по изображению
121
+ # ──────────────────────────────────────────────────────────────────────────────
122
+
123
+ def resize_by_shorter_side(image: Image.Image, target_shorter: int, multiple: int, mode: RoundingMode) -> Tuple[int, int]:
124
+ w, h = image.size
125
+ if w <= 0 or h <= 0 or target_shorter <= 0:
126
+ return w, h
127
+ scale = target_shorter / min(w, h)
128
+ new_w = ensure_multiple(int(round(w * scale)), multiple, mode)
129
+ new_h = ensure_multiple(int(round(h * scale)), multiple, mode)
130
+ return new_w, new_h
131
+
132
+ def resize_to_pixel_count(image: Image.Image, target_pixels: int, multiple: int, mode: RoundingMode) -> Tuple[int, int]:
133
+ w, h = image.size
134
+ if w <= 0 or h <= 0 or target_pixels <= 0:
135
+ return w, h
136
+ scale = math.sqrt(target_pixels / (w * h))
137
+ new_w = ensure_multiple(int(round(w * scale)), multiple, mode)
138
+ new_h = ensure_multiple(int(round(h * scale)), multiple, mode)
139
+ return new_w, new_h
140
+
141
+ # ──────────────────────────────────────────────────────────────────────────────
142
+ # Каталог разрешений: загрузка/генерация/фильтрация
143
+ # ──────────────────────────────────────────────────────────────────────────────
144
+
145
+ def load_catalog_from_file(path: str) -> List[Tuple[int, int]]:
146
+ if not os.path.isfile(path):
147
+ return []
148
+ try:
149
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
150
+ text = f.read()
151
+ return parse_dims(text)
152
+ except Exception:
153
+ return []
154
+
155
+ _GRID_CACHE: Dict[Tuple[int, int, int], List[Tuple[int, int]]] = {}
156
+
157
+ def generate_grid_catalog(min_size: int, max_size: int, step: int) -> List[Tuple[int, int]]:
158
+ """Генерация полной решётки кратных 'step' размеров в диапазоне [min_size, max_size], с кэшем."""
159
+ key = (min_size, max_size, step)
160
+ cached = _GRID_CACHE.get(key)
161
+ if cached is not None:
162
+ return cached
163
+ sizes = list(range(min_size, max_size + 1, step))
164
+ out: List[Tuple[int, int]] = []
165
+ for w in sizes:
166
+ for h in sizes:
167
+ out.append((w, h))
168
+ _GRID_CACHE[key] = out
169
+ return out
170
+
171
+ def orientation_of(w: int, h: int) -> str:
172
+ if w == h:
173
+ return "square"
174
+ return "landscape" if w > h else "portrait"
175
+
176
+ def aspect_tuple(w: int, h: int) -> Tuple[int, int]:
177
+ g = gcd(w, h)
178
+ return (w // g, h // g)
179
+
180
+ def _parse_aspect_tokens(aspect_str: str) -> List[Tuple[int, int, bool, float]]:
181
+ """
182
+ Поддерживаем токены:
183
+ - 'A:B' → точное совпадение
184
+ - '~A:B' → приблизительное (±0.02 по умолчанию)
185
+ - 'A:B@0.03' → точный A:B, но с допуском 0.03 (редкий кейс)
186
+ - '~A:B@0.015' → приблизительный матч с явным допуском
187
+ Возвращает список (ax, ay, approx, tol).
188
+ """
189
+ out: List[Tuple[int, int, bool, float]] = []
190
+ if not aspect_str.strip():
191
+ return out
192
+ tokens = [t.strip() for t in re.split(r"[;,]+", aspect_str) if t.strip()]
193
+ for t in tokens:
194
+ approx = t.startswith("~")
195
+ if approx:
196
+ t = t[1:].strip()
197
+ tol = 0.02 if approx else 0.0
198
+ m = re.match(r"(\d+)\s*:\s*(\d+)(?:\s*@\s*(0\.\d+))?$", t)
199
+ if not m:
200
+ # проигнорируем кривые токены
201
+ continue
202
+ ax, ay = int(m.group(1)), int(m.group(2))
203
+ if m.group(3):
204
+ tol = float(m.group(3))
205
+ out.append((ax, ay, approx or tol > 0.0, tol))
206
+ return out
207
+
208
+ def filter_catalog(catalog: List[Tuple[int, int]],
209
+ min_width: int, max_width: int,
210
+ min_height: int, max_height: int,
211
+ min_mp: float, max_mp: float,
212
+ multiple: int,
213
+ orientation: str, # any|square|landscape|portrait
214
+ aspect_str: str, # "", "16:9, 4:3", "~3:2", "21:9@0.03"
215
+ limit: int,
216
+ sort_key: str, # "MP", "Width", "Height"
217
+ sort_desc: bool) -> List[Dict]:
218
+ aspect_tokens = _parse_aspect_tokens(aspect_str)
219
+
220
+ rows: List[Dict] = []
221
+ for (w, h) in catalog:
222
+ if w < min_width or w > max_width:
223
+ continue
224
+ if h < min_height or h > max_height:
225
+ continue
226
+ if multiple > 1 and (w % multiple != 0 or h % multiple != 0):
227
+ continue
228
+ mp = (w * h) / 1_000_000.0
229
+ if mp < min_mp or mp > max_mp:
230
+ continue
231
+ ori = orientation_of(w, h)
232
+ if orientation != "any" and ori != orientation:
233
+ continue
234
+
235
+ # аспект-фильтр
236
+ if aspect_tokens:
237
+ w0, h0 = aspect_tuple(w, h)
238
+ ok = False
239
+ for ax, ay, approx, tol in aspect_tokens:
240
+ if approx:
241
+ if abs((w / h) - (ax / ay)) <= (tol if tol > 0 else 0.02):
242
+ ok = True
243
+ break
244
+ else:
245
+ if (w0, h0) == (ax, ay):
246
+ ok = True
247
+ break
248
+ if not ok:
249
+ continue
250
+
251
+ rows.append({
252
+ "Width": w,
253
+ "Height": h,
254
+ "Aspect": f"{aspect_tuple(w,h)[0]}:{aspect_tuple(w,h)[1]}",
255
+ "Orientation": ori,
256
+ "MP": round(mp, 3)
257
+ })
258
+
259
+ # сортировка
260
+ key = {
261
+ "MP": lambda r: r["MP"],
262
+ "Width": lambda r: r["Width"],
263
+ "Height": lambda r: r["Height"],
264
+ }.get(sort_key, lambda r: (r["MP"], r["Width"], r["Height"]))
265
+ rows.sort(key=key, reverse=sort_desc)
266
+
267
+ return rows[:limit]
268
+
269
+ def stringify_wh_list(rows: List[Dict]) -> str:
270
+ return "\n".join(f"{r['Width']}x{r['Height']}" for r in rows)
271
+
272
+ # ──────────────────────────────────────────────────────────────────────────────
273
+ # Gradio UI
274
+ # ──────────────────────────────────────────────────────────────────────────────
275
+
276
+ DEFAULT_SHORT_PRESETS = ["512", "768", "896"]
277
+ DEFAULT_PIXEL_BASES = ["512", "640", "768", "832", "896", "1024", "1152", "1216", "1344", "1536"]
278
+
279
+ ROUNDING_CHOICES: List[RoundingMode] = ["round", "floor", "ceil"]
280
+
281
+ def _parse_int_list(str_list: List[str]) -> List[int]:
282
+ out: List[int] = []
283
+ for s in str_list or []:
284
+ m = re.search(r"\d+", s)
285
+ if m:
286
+ out.append(int(m.group(0)))
287
+ return out
288
+
289
+ # ——— Вычисления по загруженному изображению ——————————————————————————————
290
+
291
+ def calculate_all(
292
+ image: Image.Image,
293
+ shorters_raw: List[str],
294
+ bases_raw: List[str],
295
+ use_autopick: bool,
296
+ model_family: ModelFamily,
297
+ diffusion_tile: int,
298
+ vae_tile: int,
299
+ manual_multiple: int,
300
+ rounding_mode: RoundingMode,
301
+ ):
302
+ if image is None:
303
+ raise gr.Error("Загрузите изображение слева.")
304
+
305
+ w, h = image.size
306
+
307
+ if use_autopick:
308
+ m_auto, reason = autopick_multiple(model_family, diffusion_tile, vae_tile, w, h)
309
+ rounding_multiple = m_auto
310
+ reason_text = f"Auto multiple = **{m_auto}** — {reason}"
311
+ else:
312
+ rounding_multiple = manual_multiple
313
+ reason_text = f"Manual multiple = **{manual_multiple}**"
314
+
315
+ shorters = _parse_int_list(shorters_raw) or _parse_int_list(DEFAULT_SHORT_PRESETS)
316
+ bases = _parse_int_list(bases_raw) or _parse_int_list(DEFAULT_PIXEL_BASES)
317
+
318
+ rows = []
319
+
320
+ # Короткая сторона
321
+ for s in shorters:
322
+ nw, nh = resize_by_shorter_side(image, s, rounding_multiple, rounding_mode)
323
+ rows.append({
324
+ "Preset": f"short={s}",
325
+ "Width": nw,
326
+ "Height": nh,
327
+ "Aspect": simplified_ratio(nw, nh),
328
+ "Pixels": nw * nh,
329
+ "MP": megapixels(nw, nh),
330
+ })
331
+
332
+ # По количеству пикселей (N^2)
333
+ for base in bases:
334
+ target_pixels = base * base
335
+ nw, nh = resize_to_pixel_count(image, target_pixels, rounding_multiple, rounding_mode)
336
+ rows.append({
337
+ "Preset": f"{base}² ({target_pixels:,})",
338
+ "Width": nw,
339
+ "Height": nh,
340
+ "Aspect": simplified_ratio(nw, nh),
341
+ "Pixels": nw * nh,
342
+ "MP": megapixels(nw, nh),
343
+ })
344
+
345
+ original_info = f"Original: {w}x{h} | Aspect {simplified_ratio(w, h)} | {megapixels(w, h)} MP"
346
+ return rows, stringify_wh_list(rows), original_info, reason_text
347
+
348
+ # ——— Каталог: загрузка/фильтр/экспорт ——————————————————————————————————————
349
+
350
+ CATALOG_PATH = os.path.join(os.getcwd(), "sdxl_resolutions_by_aspect.txt") # ищем и в /mnt/data
351
+ _LOADED_CATALOG: Optional[List[Tuple[int, int]]] = None
352
+
353
+ def _load_or_generate(default_step: int) -> Tuple[List[Tuple[int, int]], str]:
354
+ global _LOADED_CATALOG
355
+ if _LOADED_CATALOG is None:
356
+ candidates = [
357
+ CATALOG_PATH,
358
+ os.path.join(os.getcwd(), "data", "sdxl_resolutions_by_aspect.txt"),
359
+ "/mnt/data/sdxl_resolutions_by_aspect.txt",
360
+ ]
361
+ for p in candidates:
362
+ cat = load_catalog_from_file(p)
363
+ if cat:
364
+ _LOADED_CATALOG = cat
365
+ return _LOADED_CATALOG, f"Catalog: loaded from file ({len(cat)} items)."
366
+ _LOADED_CATALOG = []
367
+ return _LOADED_CATALOG, f"Catalog: no file, you can GENERATE grid (step {default_step})."
368
+
369
+ def catalog_filter_action(
370
+ use_file_catalog: bool,
371
+ generate_min: int,
372
+ generate_max: int,
373
+ generate_step: int,
374
+ filter_min_w: int,
375
+ filter_max_w: int,
376
+ filter_min_h: int,
377
+ filter_max_h: int,
378
+ filter_min_mp: float,
379
+ filter_max_mp: float,
380
+ filter_multiple: int,
381
+ filter_orientation: str,
382
+ filter_aspects: str,
383
+ filter_limit: int,
384
+ sort_key: str,
385
+ sort_desc: bool,
386
+ ):
387
+ base_list, inf = _load_or_generate(generate_step)
388
+ if use_file_catalog and base_list:
389
+ catalog = base_list
390
+ source = inf.replace("Catalog:", "Source:")
391
+ else:
392
+ # safety
393
+ generate_min = max(128, generate_min)
394
+ generate_max = min(4096, max(generate_min, generate_max))
395
+ generate_step = max(8, generate_step)
396
+ sizes = list(range(generate_min, generate_max + 1, generate_step))
397
+ est = len(sizes) ** 2
398
+ if est > 20000:
399
+ # при слишком больших объёмах — подрежем диапазон по центру
400
+ mid = (generate_min + generate_max) // 2
401
+ half = max(generate_step * 5, (generate_max - generate_min) // 6)
402
+ generate_min = max(128, mid - half)
403
+ generate_max = min(4096, mid + half)
404
+ catalog = generate_grid_catalog(generate_min, generate_max, generate_step)
405
+ source = f"Source: generated grid {generate_min}..{generate_max} step={generate_step} ({len(catalog)} combos)"
406
+
407
+ rows = filter_catalog(
408
+ catalog=catalog,
409
+ min_width=filter_min_w, max_width=filter_max_w,
410
+ min_height=filter_min_h, max_height=filter_max_h,
411
+ min_mp=filter_min_mp, max_mp=filter_max_mp,
412
+ multiple=filter_multiple,
413
+ orientation=filter_orientation,
414
+ aspect_str=filter_aspects,
415
+ limit=filter_limit,
416
+ sort_key=sort_key,
417
+ sort_desc=sort_desc,
418
+ )
419
+ return rows, stringify_wh_list(rows), source
420
+
421
+ def _rows_to_csv_bytes(rows: List[Dict]) -> bytes:
422
+ import csv
423
+ from io import StringIO
424
+ buf = StringIO()
425
+ w = csv.writer(buf)
426
+ w.writerow(["Width", "Height", "Aspect", "Orientation", "MP"])
427
+ for r in rows:
428
+ w.writerow([r["Width"], r["Height"], r["Aspect"], r["Orientation"], r["MP"]])
429
+ return buf.getvalue().encode("utf-8")
430
+
431
+ def _rows_to_json_bytes(rows: List[Dict]) -> bytes:
432
+ import json
433
+ return json.dumps(rows, ensure_ascii=False, indent=2).encode("utf-8")
434
+
435
+ def export_rows(rows: List[Dict], kind: str):
436
+ """Возвращает tuple(filename, bytes) для gr.File()"""
437
+ if not rows:
438
+ raise gr.Error("Нет данных для экспорта — сначала примените фильтр.")
439
+ if kind == "csv":
440
+ data = _rows_to_csv_bytes(rows)
441
+ name = "filtered_catalog.csv"
442
+ else:
443
+ data = _rows_to_json_bytes(rows)
444
+ name = "filtered_catalog.json"
445
+ path = os.path.join(os.getcwd(), name)
446
+ with open(path, "wb") as f:
447
+ f.write(data)
448
+ return path
449
+
450
+ # ──────────────────────────────────────────────────────────────────────────────
451
+ # UI
452
+ # ──────────────────────────────────────────────────────────────────────────────
453
+
454
+ def on_ui_tab_called():
455
+ with gr.Blocks() as ui:
456
+ gr.Markdown("### 📐 Image Ratio & Resolution Catalog Pro \n"
457
+ "Используйте файл SDXL пресетов, либо генерируйте сетку. "
458
+ "Аспекты поддерживают токены `16:9, ~3:2, 21:9@0.03, ~9:16@0.015`.")
459
+
460
+ with gr.Row():
461
+ with gr.Column(scale=1):
462
+ image = gr.Image(type="pil", source="upload", label="Изображение")
463
+ original_info = gr.Markdown("")
464
+
465
+ with gr.Column(scale=1):
466
+ # Авто/ручная кратность
467
+ use_autopick = gr.Checkbox(value=True, label="Auto multiple (рекомендуется)")
468
+ model_family = gr.Radio(
469
+ choices=["SDXL (native~1024)", "SD 1.x (native~512)", "SD 2.x (native~768)"],
470
+ value=DEFAULT_MODEL_FAMILY,
471
+ label="Model family (ручной выбор)"
472
+ )
473
+ with gr.Row():
474
+ diffusion_tile = gr.Number(value=0, precision=0, label="Diffusion tile (px, 0=нет)")
475
+ vae_tile = gr.Number(value=0, precision=0, label="VAE tile (px, 0=нет)")
476
+ manual_multiple = gr.Slider(minimum=8, maximum=256, step=8, value=64, label="Manual multiple")
477
+ rounding_mode = gr.Radio(choices=["round", "floor", "ceil"], value="round", label="Режим округления")
478
+ auto_reason = gr.Markdown("")
479
+
480
+ with gr.Row():
481
+ with gr.Column():
482
+ shorters = gr.CheckboxGroup(
483
+ choices=DEFAULT_SHORT_PRESETS,
484
+ value=DEFAULT_SHORT_PRESETS,
485
+ label="Цели по короткой стороне (px)",
486
+ )
487
+ pixel_bases = gr.CheckboxGroup(
488
+ choices=DEFAULT_PIXEL_BASES,
489
+ value=DEFAULT_PIXEL_BASES,
490
+ label="Цели по общему числу пикселей (берётся N²)",
491
+ )
492
+ run_btn = gr.Button("Рассчитать", variant="primary")
493
+
494
+ with gr.Column():
495
+ results = gr.Dataframe(
496
+ headers=["Preset", "Width", "Height", "Aspect", "Pixels", "MP"],
497
+ datatype=["str", "number", "number", "str", "number", "number"],
498
+ label="Результаты",
499
+ interactive=False,
500
+ wrap=True,
501
+ overflow_row_behaviour="paginate",
502
+ )
503
+ copy_box = gr.Textbox(label="Список WxH (для копирования)", lines=8, interactive=False)
504
+
505
+ # Каталог разрешений (импорт/генерация/фильтры)
506
+ gr.Markdown("---")
507
+ gr.Markdown("#### Каталог разрешений (импорт из файла или генерация сетки)")
508
+
509
+ with gr.Row():
510
+ use_file_catalog = gr.Checkbox(value=True, label="Использовать файл, если найден (SDXL пресеты)")
511
+ generate_min = gr.Slider(minimum=128, maximum=4096, step=8, value=512, label="Generate: min")
512
+ generate_max = gr.Slider(minimum=128, maximum=4096, step=8, value=2048, label="Generate: max")
513
+ generate_step = gr.Slider(minimum=8, maximum=256, step=8, value=64, label="Generate: step")
514
+
515
+ with gr.Row():
516
+ filter_min_w = gr.Slider(minimum=128, maximum=4096, step=8, value=512, label="min Width")
517
+ filter_max_w = gr.Slider(minimum=128, maximum=4096, step=8, value=2048, label="max Width")
518
+ filter_min_h = gr.Slider(minimum=128, maximum=4096, step=8, value=512, label="min Height")
519
+ filter_max_h = gr.Slider(minimum=128, maximum=4096, step=8, value=2048, label="max Height")
520
+
521
+ with gr.Row():
522
+ filter_min_mp = gr.Number(value=0.0, label="min MP")
523
+ filter_max_mp = gr.Number(value=8.0, label="max MP")
524
+ filter_multiple = gr.Slider(minimum=8, maximum=256, step=8, value=64, label="Требуемая кратность")
525
+ filter_orientation = gr.Dropdown(choices=["any", "square", "landscape", "portrait"], value="any", label="Ориентация")
526
+ filter_aspects = gr.Textbox(value="", label="Аспекты (пример: '16:9, ~3:2, 21:9@0.03')")
527
+
528
+ with gr.Row():
529
+ filter_limit = gr.Slider(minimum=10, maximum=5000, step=10, value=500, label="Лимит результатов")
530
+ sort_key = gr.Dropdown(choices=["MP", "Width", "Height"], value="MP", label="Сортировать по")
531
+ sort_desc = gr.Checkbox(value=True, label="По убыванию")
532
+ apply_filters = gr.Button("Применить фильтры", variant="primary")
533
+
534
+ catalog_df = gr.Dataframe(
535
+ headers=["Width", "Height", "Aspect", "Orientation", "MP"],
536
+ datatype=["number", "number", "str", "str", "number"],
537
+ label="Каталог (фильтрованный)",
538
+ interactive=False,
539
+ wrap=True,
540
+ overflow_row_behaviour="paginate",
541
+ )
542
+ catalog_copy = gr.Textbox(label="Список WxH (для копирования)", lines=12, interactive=False)
543
+ catalog_source = gr.Markdown("")
544
+
545
+ with gr.Row():
546
+ export_csv_btn = gr.Button("Скачать CSV")
547
+ export_json_btn = gr.Button("Скачать JSON")
548
+ exported_file = gr.File(label="Экспорт", visible=True)
549
+
550
+ # — Автозапуски/связи —
551
+ def _toggle_manual(m_use_auto: bool):
552
+ return gr.update(interactive=not m_use_auto)
553
+
554
+ use_autopick.change(fn=_toggle_manual, inputs=[use_autopick], outputs=[manual_multiple])
555
+
556
+ for trigger in (image.upload, run_btn.click):
557
+ trigger(
558
+ fn=calculate_all,
559
+ inputs=[image, shorters, pixel_bases, use_autopick, model_family, diffusion_tile, vae_tile, manual_multiple, rounding_mode],
560
+ outputs=[results, copy_box, original_info, auto_reason],
561
+ show_progress=False,
562
+ )
563
+
564
+ apply_filters.click(
565
+ fn=catalog_filter_action,
566
+ inputs=[
567
+ use_file_catalog, generate_min, generate_max, generate_step,
568
+ filter_min_w, filter_max_w, filter_min_h, filter_max_h,
569
+ filter_min_mp, filter_max_mp, filter_multiple,
570
+ filter_orientation, filter_aspects, filter_limit,
571
+ sort_key, sort_desc
572
+ ],
573
+ outputs=[catalog_df, catalog_copy, catalog_source],
574
+ show_progress=False
575
+ )
576
+
577
+ export_csv_btn.click(lambda rows: export_rows(rows, "csv"), inputs=[catalog_df], outputs=[exported_file])
578
+ export_json_btn.click(lambda rows: export_rows(rows, "json"), inputs=[catalog_df], outputs=[exported_file])
579
+
580
+ return (ui, "calculator+", "calculator_plus_interface"),
581
+
582
+
583
+ # ──────────────────────────────────────────────────────────────────────────────
584
+ # Регистрация в A1111 или автономный запуск
585
+ # ──────────────────────────────────────────────────────────────────────────────
586
+ try:
587
+ from modules import script_callbacks # type: ignore
588
+ script_callbacks.on_ui_tabs(on_ui_tab_called)
589
+ except (ImportError, ModuleNotFoundError):
590
+ if __name__ == "__main__":
591
+ interface, _, _ = on_ui_tab_called()[0]
592
+ interface.launch()
sd-webui-convenience-util/scripts/sdxl_resolutions_by_aspect.txt ADDED
@@ -0,0 +1,2289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Stable Diffusion XL – Полный список разрешений (512–2048, шаг 64)
2
+
3
+ Всего комбинаций: 625
4
+
5
+ ======================================================================
6
+
7
+
8
+ === Квадратное ===
9
+
10
+
11
+ Соотношение 1:1:
12
+
13
+ 512x512, 576x576, 640x640, 704x704, 768x768, 832x832, 896x896, 960x960, 1024x1024, 1088x1088, 1152x1152, 1216x1216, 1280x1280, 1344x1344, 1408x1408, 1472x1472, 1536x1536, 1600x1600, 1664x1664, 1728x1728, 1792x1792, 1856x1856, 1920x1920, 1984x1984, 2048x2048
14
+
15
+
16
+ === Альбомное (Landscape) ===
17
+
18
+
19
+ Соотношение 32:31:
20
+
21
+ 2048x1984
22
+
23
+
24
+ Соотношение 31:30:
25
+
26
+ 1984x1920
27
+
28
+
29
+ Соотношение 30:29:
30
+
31
+ 1920x1856
32
+
33
+
34
+ Соотношение 29:28:
35
+
36
+ 1856x1792
37
+
38
+
39
+ Соотношение 28:27:
40
+
41
+ 1792x1728
42
+
43
+
44
+ Соотношение 27:26:
45
+
46
+ 1728x1664
47
+
48
+
49
+ Соотношение 26:25:
50
+
51
+ 1664x1600
52
+
53
+
54
+ Соотношение 25:24:
55
+
56
+ 1600x1536
57
+
58
+
59
+ Соотношение 24:23:
60
+
61
+ 1536x1472
62
+
63
+
64
+ Соотношение 23:22:
65
+
66
+ 1472x1408
67
+
68
+
69
+ Соотношение 22:21:
70
+
71
+ 1408x1344
72
+
73
+
74
+ Соотношение 21:20:
75
+
76
+ 1344x1280
77
+
78
+
79
+ Соотношение 20:19:
80
+
81
+ 1280x1216
82
+
83
+
84
+ Соотношение 19:18:
85
+
86
+ 1216x1152
87
+
88
+
89
+ Соотношение 18:17:
90
+
91
+ 1152x1088
92
+
93
+
94
+ Соотношение 17:16:
95
+
96
+ 1088x1024
97
+
98
+
99
+ Соотношение 16:15:
100
+
101
+ 1024x960, 2048x1920
102
+
103
+
104
+ Соотношение 31:29:
105
+
106
+ 1984x1856
107
+
108
+
109
+ Соотношение 15:14:
110
+
111
+ 960x896, 1920x1792
112
+
113
+
114
+ Соотношение 29:27:
115
+
116
+ 1856x1728
117
+
118
+
119
+ Соотношение 14:13:
120
+
121
+ 896x832, 1792x1664
122
+
123
+
124
+ Соотношение 27:25:
125
+
126
+ 1728x1600
127
+
128
+
129
+ Соотношение 13:12:
130
+
131
+ 832x768, 1664x1536
132
+
133
+
134
+ Соотношение 25:23:
135
+
136
+ 1600x1472
137
+
138
+
139
+ Соотношение 12:11:
140
+
141
+ 768x704, 1536x1408
142
+
143
+
144
+ Соотношение 23:21:
145
+
146
+ 1472x1344
147
+
148
+
149
+ Соотношение 11:10:
150
+
151
+ 704x640, 1408x1280
152
+
153
+
154
+ Соотношение 32:29:
155
+
156
+ 2048x1856
157
+
158
+
159
+ Соотношение 21:19:
160
+
161
+ 1344x1216
162
+
163
+
164
+ Соотношение 31:28:
165
+
166
+ 1984x1792
167
+
168
+
169
+ Соотношение 10:9:
170
+
171
+ 640x576, 1280x1152, 1920x1728
172
+
173
+
174
+ Соотношение 29:26:
175
+
176
+ 1856x1664
177
+
178
+
179
+ Соотношение 19:17:
180
+
181
+ 1216x1088
182
+
183
+
184
+ Соотношение 28:25:
185
+
186
+ 1792x1600
187
+
188
+
189
+ Соотношение 9:8:
190
+
191
+ 576x512, 1152x1024, 1728x1536
192
+
193
+
194
+ Соотношение 26:23:
195
+
196
+ 1664x1472
197
+
198
+
199
+ Соотношение 17:15:
200
+
201
+ 1088x960
202
+
203
+
204
+ Соотношение 25:22:
205
+
206
+ 1600x1408
207
+
208
+
209
+ Соотношение 8:7:
210
+
211
+ 1024x896, 1536x1344, 2048x1792
212
+
213
+
214
+ Соотношение 31:27:
215
+
216
+ 1984x1728
217
+
218
+
219
+ Соотношение 23:20:
220
+
221
+ 1472x1280
222
+
223
+
224
+ Соотношение 15:13:
225
+
226
+ 960x832, 1920x1664
227
+
228
+
229
+ Соотношение 22:19:
230
+
231
+ 1408x1216
232
+
233
+
234
+ Соотношение 29:25:
235
+
236
+ 1856x1600
237
+
238
+
239
+ Соотношение 7:6:
240
+
241
+ 896x768, 1344x1152, 1792x1536
242
+
243
+
244
+ Соотношение 27:23:
245
+
246
+ 1728x1472
247
+
248
+
249
+ Соотношение 20:17:
250
+
251
+ 1280x1088
252
+
253
+
254
+ Соотношение 13:11:
255
+
256
+ 832x704, 1664x1408
257
+
258
+
259
+ Соотношение 32:27:
260
+
261
+ 2048x1728
262
+
263
+
264
+ Соотношение 19:16:
265
+
266
+ 1216x1024
267
+
268
+
269
+ Соотношение 25:21:
270
+
271
+ 1600x1344
272
+
273
+
274
+ Соотношение 31:26:
275
+
276
+ 1984x1664
277
+
278
+
279
+ Соотношение 6:5:
280
+
281
+ 768x640, 1152x960, 1536x1280, 1920x1600
282
+
283
+
284
+ Соотношение 29:24:
285
+
286
+ 1856x1536
287
+
288
+
289
+ Соотношение 23:19:
290
+
291
+ 1472x1216
292
+
293
+
294
+ Соотношение 17:14:
295
+
296
+ 1088x896
297
+
298
+
299
+ Соотношение 28:23:
300
+
301
+ 1792x1472
302
+
303
+
304
+ Соотношение 11:9:
305
+
306
+ 704x576, 1408x1152
307
+
308
+
309
+ Соотношение 27:22:
310
+
311
+ 1728x1408
312
+
313
+
314
+ Соотношение 16:13:
315
+
316
+ 1024x832, 2048x1664
317
+
318
+
319
+ Соотношение 21:17:
320
+
321
+ 1344x1088
322
+
323
+
324
+ Соотношение 26:21:
325
+
326
+ 1664x1344
327
+
328
+
329
+ Соотношение 31:25:
330
+
331
+ 1984x1600
332
+
333
+
334
+ Соотношение 5:4:
335
+
336
+ 640x512, 960x768, 1280x1024, 1600x1280, 1920x1536
337
+
338
+
339
+ Соотношение 29:23:
340
+
341
+ 1856x1472
342
+
343
+
344
+ Соотношение 24:19:
345
+
346
+ 1536x1216
347
+
348
+
349
+ Соотношение 19:15:
350
+
351
+ 1216x960
352
+
353
+
354
+ Соотношение 14:11:
355
+
356
+ 896x704, 1792x1408
357
+
358
+
359
+ Соотношение 23:18:
360
+
361
+ 1472x1152
362
+
363
+
364
+ Соотношение 32:25:
365
+
366
+ 2048x1600
367
+
368
+
369
+ Соотношение 9:7:
370
+
371
+ 1152x896, 1728x1344
372
+
373
+
374
+ Соотношение 31:24:
375
+
376
+ 1984x1536
377
+
378
+
379
+ Соотношение 22:17:
380
+
381
+ 1408x1088
382
+
383
+
384
+ Соотношение 13:10:
385
+
386
+ 832x640, 1664x1280
387
+
388
+
389
+ Соотношение 30:23:
390
+
391
+ 1920x1472
392
+
393
+
394
+ Соотношение 17:13:
395
+
396
+ 1088x832
397
+
398
+
399
+ Соотношение 21:16:
400
+
401
+ 1344x1024
402
+
403
+
404
+ Соотношение 25:19:
405
+
406
+ 1600x1216
407
+
408
+
409
+ Соотношение 29:22:
410
+
411
+ 1856x1408
412
+
413
+
414
+ Соотношение 4:3:
415
+
416
+ 768x576, 1024x768, 1280x960, 1536x1152, 1792x1344, 2048x1536
417
+
418
+
419
+ Соотношение 31:23:
420
+
421
+ 1984x1472
422
+
423
+
424
+ Соотношение 27:20:
425
+
426
+ 1728x1280
427
+
428
+
429
+ Соотношение 23:17:
430
+
431
+ 1472x1088
432
+
433
+
434
+ Соотношение 19:14:
435
+
436
+ 1216x896
437
+
438
+
439
+ Соотношение 15:11:
440
+
441
+ 960x704, 1920x1408
442
+
443
+
444
+ Соотношение 26:19:
445
+
446
+ 1664x1216
447
+
448
+
449
+ Соотношение 11:8:
450
+
451
+ 704x512, 1408x1024
452
+
453
+
454
+ Соотношение 29:21:
455
+
456
+ 1856x1344
457
+
458
+
459
+ Соотношение 18:13:
460
+
461
+ 1152x832
462
+
463
+
464
+ Соотношение 25:18:
465
+
466
+ 1600x1152
467
+
468
+
469
+ Соотношение 32:23:
470
+
471
+ 2048x1472
472
+
473
+
474
+ Соотношение 7:5:
475
+
476
+ 896x640, 1344x960, 1792x1280
477
+
478
+
479
+ Соотношение 31:22:
480
+
481
+ 1984x1408
482
+
483
+
484
+ Соотношение 24:17:
485
+
486
+ 1536x1088
487
+
488
+
489
+ Соотношение 17:12:
490
+
491
+ 1088x768
492
+
493
+
494
+ Соотношение 27:19:
495
+
496
+ 1728x1216
497
+
498
+
499
+ Соотношение 10:7:
500
+
501
+ 1280x896, 1920x1344
502
+
503
+
504
+ Соотношение 23:16:
505
+
506
+ 1472x1024
507
+
508
+
509
+ Соотношение 13:9:
510
+
511
+ 832x576, 1664x1152
512
+
513
+
514
+ Соотношение 29:20:
515
+
516
+ 1856x1280
517
+
518
+
519
+ Соотношение 16:11:
520
+
521
+ 1024x704, 2048x1408
522
+
523
+
524
+ Соотношение 19:13:
525
+
526
+ 1216x832
527
+
528
+
529
+ Соотношение 22:15:
530
+
531
+ 1408x960
532
+
533
+
534
+ Соотношение 25:17:
535
+
536
+ 1600x1088
537
+
538
+
539
+ Соотношение 28:19:
540
+
541
+ 1792x1216
542
+
543
+
544
+ Соотношение 31:21:
545
+
546
+ 1984x1344
547
+
548
+
549
+ Соотношение 3:2:
550
+
551
+ 768x512, 960x640, 1152x768, 1344x896, 1536x1024, 1728x1152, 1920x1280
552
+
553
+
554
+ Соотношение 32:21:
555
+
556
+ 2048x1344
557
+
558
+
559
+ Соотношение 29:19:
560
+
561
+ 1856x1216
562
+
563
+
564
+ Соотношение 26:17:
565
+
566
+ 1664x1088
567
+
568
+
569
+ Соотношение 23:15:
570
+
571
+ 1472x960
572
+
573
+
574
+ Соотношение 20:13:
575
+
576
+ 1280x832
577
+
578
+
579
+ Соотношение 17:11:
580
+
581
+ 1088x704
582
+
583
+
584
+ Соотношение 31:20:
585
+
586
+ 1984x1280
587
+
588
+
589
+ Соотношение 14:9:
590
+
591
+ 896x576, 1792x1152
592
+
593
+
594
+ Соотношение 25:16:
595
+
596
+ 1600x1024
597
+
598
+
599
+ Соотношение 11:7:
600
+
601
+ 1408x896
602
+
603
+
604
+ Соотношение 30:19:
605
+
606
+ 1920x1216
607
+
608
+
609
+ Соотношение 19:12:
610
+
611
+ 1216x768
612
+
613
+
614
+ Соотношение 27:17:
615
+
616
+ 1728x1088
617
+
618
+
619
+ Соотношение 8:5:
620
+
621
+ 1024x640, 1536x960, 2048x1280
622
+
623
+
624
+ Соотношение 29:18:
625
+
626
+ 1856x1152
627
+
628
+
629
+ Соотношение 21:13:
630
+
631
+ 1344x832
632
+
633
+
634
+ Соотношение 13:8:
635
+
636
+ 832x512, 1664x1024
637
+
638
+
639
+ Соотношение 31:19:
640
+
641
+ 1984x1216
642
+
643
+
644
+ Соотношение 18:11:
645
+
646
+ 1152x704
647
+
648
+
649
+ Соотношение 23:14:
650
+
651
+ 1472x896
652
+
653
+
654
+ Соотношение 28:17:
655
+
656
+ 1792x1088
657
+
658
+
659
+ Соотношение 5:3:
660
+
661
+ 960x576, 1280x768, 1600x960, 1920x1152
662
+
663
+
664
+ Соотношение 32:19:
665
+
666
+ 2048x1216
667
+
668
+
669
+ Соотношение 27:16:
670
+
671
+ 1728x1024
672
+
673
+
674
+ Соотношение 22:13:
675
+
676
+ 1408x832
677
+
678
+
679
+ Соотношение 17:10:
680
+
681
+ 1088x640
682
+
683
+
684
+ Соотношение 29:17:
685
+
686
+ 1856x1088
687
+
688
+
689
+ Соотношение 12:7:
690
+
691
+ 1536x896
692
+
693
+
694
+ Соотношение 31:18:
695
+
696
+ 1984x1152
697
+
698
+
699
+ Соотношение 19:11:
700
+
701
+ 1216x704
702
+
703
+
704
+ Соотношение 26:15:
705
+
706
+ 1664x960
707
+
708
+
709
+ Соотношение 7:4:
710
+
711
+ 896x512, 1344x768, 1792x1024
712
+
713
+
714
+ Соотношение 30:17:
715
+
716
+ 1920x1088
717
+
718
+
719
+ Соотношение 23:13:
720
+
721
+ 1472x832
722
+
723
+
724
+ Соотношение 16:9:
725
+
726
+ 1024x576, 2048x1152
727
+
728
+
729
+ Соотношение 25:14:
730
+
731
+ 1600x896
732
+
733
+
734
+ Соотношение 9:5:
735
+
736
+ 1152x640, 1728x960
737
+
738
+
739
+ Соотношение 29:16:
740
+
741
+ 1856x1024
742
+
743
+
744
+ Соотношение 20:11:
745
+
746
+ 1280x704
747
+
748
+
749
+ Соотношение 31:17:
750
+
751
+ 1984x1088
752
+
753
+
754
+ Соотношение 11:6:
755
+
756
+ 1408x768
757
+
758
+
759
+ Соотношение 24:13:
760
+
761
+ 1536x832
762
+
763
+
764
+ Соотношение 13:7:
765
+
766
+ 1664x896
767
+
768
+
769
+ Соотношение 28:15:
770
+
771
+ 1792x960
772
+
773
+
774
+ Соотношение 15:8:
775
+
776
+ 960x512, 1920x1024
777
+
778
+
779
+ Соотношение 32:17:
780
+
781
+ 2048x1088
782
+
783
+
784
+ Соотношение 17:9:
785
+
786
+ 1088x576
787
+
788
+
789
+ Соотношение 19:10:
790
+
791
+ 1216x640
792
+
793
+
794
+ Соотношение 21:11:
795
+
796
+ 1344x704
797
+
798
+
799
+ Соотношение 23:12:
800
+
801
+ 1472x768
802
+
803
+
804
+ Соотношение 25:13:
805
+
806
+ 1600x832
807
+
808
+
809
+ Соотношение 27:14:
810
+
811
+ 1728x896
812
+
813
+
814
+ Соотношение 29:15:
815
+
816
+ 1856x960
817
+
818
+
819
+ Соотношение 31:16:
820
+
821
+ 1984x1024
822
+
823
+
824
+ Соотношение 2:1:
825
+
826
+ 1024x512, 1152x576, 1280x640, 1408x704, 1536x768, 1664x832, 1792x896, 1920x960, 2048x1024
827
+
828
+
829
+ Соотношение 31:15:
830
+
831
+ 1984x960
832
+
833
+
834
+ Соотношение 29:14:
835
+
836
+ 1856x896
837
+
838
+
839
+ Соотношение 27:13:
840
+
841
+ 1728x832
842
+
843
+
844
+ Соотношение 25:12:
845
+
846
+ 1600x768
847
+
848
+
849
+ Соотношение 23:11:
850
+
851
+ 1472x704
852
+
853
+
854
+ Соотношение 21:10:
855
+
856
+ 1344x640
857
+
858
+
859
+ Соотношение 19:9:
860
+
861
+ 1216x576
862
+
863
+
864
+ Соотношение 17:8:
865
+
866
+ 1088x512
867
+
868
+
869
+ Соотношение 32:15:
870
+
871
+ 2048x960
872
+
873
+
874
+ Соотношение 15:7:
875
+
876
+ 1920x896
877
+
878
+
879
+ Соотношение 28:13:
880
+
881
+ 1792x832
882
+
883
+
884
+ Соотношение 13:6:
885
+
886
+ 1664x768
887
+
888
+
889
+ Соотношение 24:11:
890
+
891
+ 1536x704
892
+
893
+
894
+ Соотношение 11:5:
895
+
896
+ 1408x640
897
+
898
+
899
+ Соотношение 31:14:
900
+
901
+ 1984x896
902
+
903
+
904
+ Соотношение 20:9:
905
+
906
+ 1280x576
907
+
908
+
909
+ Соотношение 29:13:
910
+
911
+ 1856x832
912
+
913
+
914
+ Соотношение 9:4:
915
+
916
+ 1152x512, 1728x768
917
+
918
+
919
+ Соотношение 25:11:
920
+
921
+ 1600x704
922
+
923
+
924
+ Соотношение 16:7:
925
+
926
+ 2048x896
927
+
928
+
929
+ Соотношение 23:10:
930
+
931
+ 1472x640
932
+
933
+
934
+ Соотношение 30:13:
935
+
936
+ 1920x832
937
+
938
+
939
+ Соотношение 7:3:
940
+
941
+ 1344x576, 1792x768
942
+
943
+
944
+ Соотношение 26:11:
945
+
946
+ 1664x704
947
+
948
+
949
+ Соотношение 19:8:
950
+
951
+ 1216x512
952
+
953
+
954
+ Соотношение 31:13:
955
+
956
+ 1984x832
957
+
958
+
959
+ Соотношение 12:5:
960
+
961
+ 1536x640
962
+
963
+
964
+ Соотношение 29:12:
965
+
966
+ 1856x768
967
+
968
+
969
+ Соотношение 22:9:
970
+
971
+ 1408x576
972
+
973
+
974
+ Соотношение 27:11:
975
+
976
+ 1728x704
977
+
978
+
979
+ Соотношение 32:13:
980
+
981
+ 2048x832
982
+
983
+
984
+ Соотношение 5:2:
985
+
986
+ 1280x512, 1600x640, 1920x768
987
+
988
+
989
+ Соотношение 28:11:
990
+
991
+ 1792x704
992
+
993
+
994
+ Соотношение 23:9:
995
+
996
+ 1472x576
997
+
998
+
999
+ Соотношение 31:12:
1000
+
1001
+ 1984x768
1002
+
1003
+
1004
+ Соотношение 13:5:
1005
+
1006
+ 1664x640
1007
+
1008
+
1009
+ Соотношение 21:8:
1010
+
1011
+ 1344x512
1012
+
1013
+
1014
+ Соотношение 29:11:
1015
+
1016
+ 1856x704
1017
+
1018
+
1019
+ Соотношение 8:3:
1020
+
1021
+ 1536x576, 2048x768
1022
+
1023
+
1024
+ Соотношение 27:10:
1025
+
1026
+ 1728x640
1027
+
1028
+
1029
+ Соотношение 30:11:
1030
+
1031
+ 1920x704
1032
+
1033
+
1034
+ Соотношение 11:4:
1035
+
1036
+ 1408x512
1037
+
1038
+
1039
+ Соотношение 25:9:
1040
+
1041
+ 1600x576
1042
+
1043
+
1044
+ Соотношение 14:5:
1045
+
1046
+ 1792x640
1047
+
1048
+
1049
+ Соотношение 31:11:
1050
+
1051
+ 1984x704
1052
+
1053
+
1054
+ Соотношение 23:8:
1055
+
1056
+ 1472x512
1057
+
1058
+
1059
+ Соотношение 26:9:
1060
+
1061
+ 1664x576
1062
+
1063
+
1064
+ Соотношение 29:10:
1065
+
1066
+ 1856x640
1067
+
1068
+
1069
+ Соотношение 32:11:
1070
+
1071
+ 2048x704
1072
+
1073
+
1074
+ Соотношение 3:1:
1075
+
1076
+ 1536x512, 1728x576, 1920x640
1077
+
1078
+
1079
+ Соотношение 31:10:
1080
+
1081
+ 1984x640
1082
+
1083
+
1084
+ Соотношение 28:9:
1085
+
1086
+ 1792x576
1087
+
1088
+
1089
+ Соотношение 25:8:
1090
+
1091
+ 1600x512
1092
+
1093
+
1094
+ Соотношение 16:5:
1095
+
1096
+ 2048x640
1097
+
1098
+
1099
+ Соотношение 29:9:
1100
+
1101
+ 1856x576
1102
+
1103
+
1104
+ Соотношение 13:4:
1105
+
1106
+ 1664x512
1107
+
1108
+
1109
+ Соотношение 10:3:
1110
+
1111
+ 1920x576
1112
+
1113
+
1114
+ Соотношение 27:8:
1115
+
1116
+ 1728x512
1117
+
1118
+
1119
+ Соотношение 31:9:
1120
+
1121
+ 1984x576
1122
+
1123
+
1124
+ Соотношение 7:2:
1125
+
1126
+ 1792x512
1127
+
1128
+
1129
+ Соотношение 32:9:
1130
+
1131
+ 2048x576
1132
+
1133
+
1134
+ Соотношение 29:8:
1135
+
1136
+ 1856x512
1137
+
1138
+
1139
+ Соотношение 15:4:
1140
+
1141
+ 1920x512
1142
+
1143
+
1144
+ Соотношение 31:8:
1145
+
1146
+ 1984x512
1147
+
1148
+
1149
+ Соотношение 4:1:
1150
+
1151
+ 2048x512
1152
+
1153
+
1154
+ === Портретное (Portrait) ===
1155
+
1156
+
1157
+ Соотношение 1:4:
1158
+
1159
+ 512x2048
1160
+
1161
+
1162
+ Соотношение 8:31:
1163
+
1164
+ 512x1984
1165
+
1166
+
1167
+ Соотношение 4:15:
1168
+
1169
+ 512x1920
1170
+
1171
+
1172
+ Соотношение 8:29:
1173
+
1174
+ 512x1856
1175
+
1176
+
1177
+ Соотношение 9:32:
1178
+
1179
+ 576x2048
1180
+
1181
+
1182
+ Соотношение 2:7:
1183
+
1184
+ 512x1792
1185
+
1186
+
1187
+ Соотношение 9:31:
1188
+
1189
+ 576x1984
1190
+
1191
+
1192
+ Соотношение 8:27:
1193
+
1194
+ 512x1728
1195
+
1196
+
1197
+ Соотношение 3:10:
1198
+
1199
+ 576x1920
1200
+
1201
+
1202
+ Соотношение 4:13:
1203
+
1204
+ 512x1664
1205
+
1206
+
1207
+ Соотношение 9:29:
1208
+
1209
+ 576x1856
1210
+
1211
+
1212
+ Соотношение 5:16:
1213
+
1214
+ 640x2048
1215
+
1216
+
1217
+ Соотношение 8:25:
1218
+
1219
+ 512x1600
1220
+
1221
+
1222
+ Соотношение 9:28:
1223
+
1224
+ 576x1792
1225
+
1226
+
1227
+ Соотношение 10:31:
1228
+
1229
+ 640x1984
1230
+
1231
+
1232
+ Соотношение 1:3:
1233
+
1234
+ 512x1536, 576x1728, 640x1920
1235
+
1236
+
1237
+ Соотношение 11:32:
1238
+
1239
+ 704x2048
1240
+
1241
+
1242
+ Соотношение 10:29:
1243
+
1244
+ 640x1856
1245
+
1246
+
1247
+ Соотношение 9:26:
1248
+
1249
+ 576x1664
1250
+
1251
+
1252
+ Соотношение 8:23:
1253
+
1254
+ 512x1472
1255
+
1256
+
1257
+ Соотношение 11:31:
1258
+
1259
+ 704x1984
1260
+
1261
+
1262
+ Соотношение 5:14:
1263
+
1264
+ 640x1792
1265
+
1266
+
1267
+ Соотношение 9:25:
1268
+
1269
+ 576x1600
1270
+
1271
+
1272
+ Соотношение 4:11:
1273
+
1274
+ 512x1408
1275
+
1276
+
1277
+ Соотношение 11:30:
1278
+
1279
+ 704x1920
1280
+
1281
+
1282
+ Соотношение 10:27:
1283
+
1284
+ 640x1728
1285
+
1286
+
1287
+ Соотношение 3:8:
1288
+
1289
+ 576x1536, 768x2048
1290
+
1291
+
1292
+ Соотношение 11:29:
1293
+
1294
+ 704x1856
1295
+
1296
+
1297
+ Соотношение 8:21:
1298
+
1299
+ 512x1344
1300
+
1301
+
1302
+ Соотношение 5:13:
1303
+
1304
+ 640x1664
1305
+
1306
+
1307
+ Соотношение 12:31:
1308
+
1309
+ 768x1984
1310
+
1311
+
1312
+ Соотношение 9:23:
1313
+
1314
+ 576x1472
1315
+
1316
+
1317
+ Соотношение 11:28:
1318
+
1319
+ 704x1792
1320
+
1321
+
1322
+ Соотношение 2:5:
1323
+
1324
+ 512x1280, 640x1600, 768x1920
1325
+
1326
+
1327
+ Соотношение 13:32:
1328
+
1329
+ 832x2048
1330
+
1331
+
1332
+ Соотношение 11:27:
1333
+
1334
+ 704x1728
1335
+
1336
+
1337
+ Соотношение 9:22:
1338
+
1339
+ 576x1408
1340
+
1341
+
1342
+ Соотношение 12:29:
1343
+
1344
+ 768x1856
1345
+
1346
+
1347
+ Соотношение 5:12:
1348
+
1349
+ 640x1536
1350
+
1351
+
1352
+ Соотношение 13:31:
1353
+
1354
+ 832x1984
1355
+
1356
+
1357
+ Соотношение 8:19:
1358
+
1359
+ 512x1216
1360
+
1361
+
1362
+ Соотношение 11:26:
1363
+
1364
+ 704x1664
1365
+
1366
+
1367
+ Соотношение 3:7:
1368
+
1369
+ 576x1344, 768x1792
1370
+
1371
+
1372
+ Соотношение 13:30:
1373
+
1374
+ 832x1920
1375
+
1376
+
1377
+ Соотношение 10:23:
1378
+
1379
+ 640x1472
1380
+
1381
+
1382
+ Соотношение 7:16:
1383
+
1384
+ 896x2048
1385
+
1386
+
1387
+ Соотношение 11:25:
1388
+
1389
+ 704x1600
1390
+
1391
+
1392
+ Соотношение 4:9:
1393
+
1394
+ 512x1152, 768x1728
1395
+
1396
+
1397
+ Соотношение 13:29:
1398
+
1399
+ 832x1856
1400
+
1401
+
1402
+ Соотношение 9:20:
1403
+
1404
+ 576x1280
1405
+
1406
+
1407
+ Соотношение 14:31:
1408
+
1409
+ 896x1984
1410
+
1411
+
1412
+ Соотношение 5:11:
1413
+
1414
+ 640x1408
1415
+
1416
+
1417
+ Соотношение 11:24:
1418
+
1419
+ 704x1536
1420
+
1421
+
1422
+ Соотношение 6:13:
1423
+
1424
+ 768x1664
1425
+
1426
+
1427
+ Соотношение 13:28:
1428
+
1429
+ 832x1792
1430
+
1431
+
1432
+ Соотношение 7:15:
1433
+
1434
+ 896x1920
1435
+
1436
+
1437
+ Соотношение 15:32:
1438
+
1439
+ 960x2048
1440
+
1441
+
1442
+ Соотношение 8:17:
1443
+
1444
+ 512x1088
1445
+
1446
+
1447
+ Соотношение 9:19:
1448
+
1449
+ 576x1216
1450
+
1451
+
1452
+ Соотношение 10:21:
1453
+
1454
+ 640x1344
1455
+
1456
+
1457
+ Соотношение 11:23:
1458
+
1459
+ 704x1472
1460
+
1461
+
1462
+ Соотношение 12:25:
1463
+
1464
+ 768x1600
1465
+
1466
+
1467
+ Соотношение 13:27:
1468
+
1469
+ 832x1728
1470
+
1471
+
1472
+ Соотношение 14:29:
1473
+
1474
+ 896x1856
1475
+
1476
+
1477
+ Соотношение 15:31:
1478
+
1479
+ 960x1984
1480
+
1481
+
1482
+ Соотношение 1:2:
1483
+
1484
+ 512x1024, 576x1152, 640x1280, 704x1408, 768x1536, 832x1664, 896x1792, 960x1920, 1024x2048
1485
+
1486
+
1487
+ Соотношение 16:31:
1488
+
1489
+ 1024x1984
1490
+
1491
+
1492
+ Соотношение 15:29:
1493
+
1494
+ 960x1856
1495
+
1496
+
1497
+ Соотношение 14:27:
1498
+
1499
+ 896x1728
1500
+
1501
+
1502
+ Соотношение 13:25:
1503
+
1504
+ 832x1600
1505
+
1506
+
1507
+ Соотношение 12:23:
1508
+
1509
+ 768x1472
1510
+
1511
+
1512
+ Соотношение 11:21:
1513
+
1514
+ 704x1344
1515
+
1516
+
1517
+ Соотношение 10:19:
1518
+
1519
+ 640x1216
1520
+
1521
+
1522
+ Соотношение 9:17:
1523
+
1524
+ 576x1088
1525
+
1526
+
1527
+ Соотношение 17:32:
1528
+
1529
+ 1088x2048
1530
+
1531
+
1532
+ Соотношение 8:15:
1533
+
1534
+ 512x960, 1024x1920
1535
+
1536
+
1537
+ Соотношение 15:28:
1538
+
1539
+ 960x1792
1540
+
1541
+
1542
+ Соотношение 7:13:
1543
+
1544
+ 896x1664
1545
+
1546
+
1547
+ Соотношение 13:24:
1548
+
1549
+ 832x1536
1550
+
1551
+
1552
+ Соотношение 6:11:
1553
+
1554
+ 768x1408
1555
+
1556
+
1557
+ Соотношение 17:31:
1558
+
1559
+ 1088x1984
1560
+
1561
+
1562
+ Соотношение 11:20:
1563
+
1564
+ 704x1280
1565
+
1566
+
1567
+ Соотношение 16:29:
1568
+
1569
+ 1024x1856
1570
+
1571
+
1572
+ Соотношение 5:9:
1573
+
1574
+ 640x1152, 960x1728
1575
+
1576
+
1577
+ Соотношение 14:25:
1578
+
1579
+ 896x1600
1580
+
1581
+
1582
+ Соотношение 9:16:
1583
+
1584
+ 576x1024, 1152x2048
1585
+
1586
+
1587
+ Соотношение 13:23:
1588
+
1589
+ 832x1472
1590
+
1591
+
1592
+ Соотношение 17:30:
1593
+
1594
+ 1088x1920
1595
+
1596
+
1597
+ Соотношение 4:7:
1598
+
1599
+ 512x896, 768x1344, 1024x1792
1600
+
1601
+
1602
+ Соотношение 15:26:
1603
+
1604
+ 960x1664
1605
+
1606
+
1607
+ Соотношение 11:19:
1608
+
1609
+ 704x1216
1610
+
1611
+
1612
+ Соотношение 18:31:
1613
+
1614
+ 1152x1984
1615
+
1616
+
1617
+ Соотношение 7:12:
1618
+
1619
+ 896x1536
1620
+
1621
+
1622
+ Соотношение 17:29:
1623
+
1624
+ 1088x1856
1625
+
1626
+
1627
+ Соотношение 10:17:
1628
+
1629
+ 640x1088
1630
+
1631
+
1632
+ Соотношение 13:22:
1633
+
1634
+ 832x1408
1635
+
1636
+
1637
+ Соотношение 16:27:
1638
+
1639
+ 1024x1728
1640
+
1641
+
1642
+ Соотношение 19:32:
1643
+
1644
+ 1216x2048
1645
+
1646
+
1647
+ Соотношение 3:5:
1648
+
1649
+ 576x960, 768x1280, 960x1600, 1152x1920
1650
+
1651
+
1652
+ Соотношение 17:28:
1653
+
1654
+ 1088x1792
1655
+
1656
+
1657
+ Соотношение 14:23:
1658
+
1659
+ 896x1472
1660
+
1661
+
1662
+ Соотношение 11:18:
1663
+
1664
+ 704x1152
1665
+
1666
+
1667
+ Соотношение 19:31:
1668
+
1669
+ 1216x1984
1670
+
1671
+
1672
+ Соотношение 8:13:
1673
+
1674
+ 512x832, 1024x1664
1675
+
1676
+
1677
+ Соотношение 13:21:
1678
+
1679
+ 832x1344
1680
+
1681
+
1682
+ Соотношение 18:29:
1683
+
1684
+ 1152x1856
1685
+
1686
+
1687
+ Соотношение 5:8:
1688
+
1689
+ 640x1024, 960x1536, 1280x2048
1690
+
1691
+
1692
+ Соотношение 17:27:
1693
+
1694
+ 1088x1728
1695
+
1696
+
1697
+ Соотношение 12:19:
1698
+
1699
+ 768x1216
1700
+
1701
+
1702
+ Соотношение 19:30:
1703
+
1704
+ 1216x1920
1705
+
1706
+
1707
+ Соотношение 7:11:
1708
+
1709
+ 896x1408
1710
+
1711
+
1712
+ Соотношение 16:25:
1713
+
1714
+ 1024x1600
1715
+
1716
+
1717
+ Соотношение 9:14:
1718
+
1719
+ 576x896, 1152x1792
1720
+
1721
+
1722
+ Соотношение 20:31:
1723
+
1724
+ 1280x1984
1725
+
1726
+
1727
+ Соотношение 11:17:
1728
+
1729
+ 704x1088
1730
+
1731
+
1732
+ Соотношение 13:20:
1733
+
1734
+ 832x1280
1735
+
1736
+
1737
+ Соотношение 15:23:
1738
+
1739
+ 960x1472
1740
+
1741
+
1742
+ Соотношение 17:26:
1743
+
1744
+ 1088x1664
1745
+
1746
+
1747
+ Соотношение 19:29:
1748
+
1749
+ 1216x1856
1750
+
1751
+
1752
+ Соотношение 21:32:
1753
+
1754
+ 1344x2048
1755
+
1756
+
1757
+ Соотношение 2:3:
1758
+
1759
+ 512x768, 640x960, 768x1152, 896x1344, 1024x1536, 1152x1728, 1280x1920
1760
+
1761
+
1762
+ Соотношение 21:31:
1763
+
1764
+ 1344x1984
1765
+
1766
+
1767
+ Соотношение 19:28:
1768
+
1769
+ 1216x1792
1770
+
1771
+
1772
+ Соотношение 17:25:
1773
+
1774
+ 1088x1600
1775
+
1776
+
1777
+ Соотношение 15:22:
1778
+
1779
+ 960x1408
1780
+
1781
+
1782
+ Соотношение 13:19:
1783
+
1784
+ 832x1216
1785
+
1786
+
1787
+ Соотношение 11:16:
1788
+
1789
+ 704x1024, 1408x2048
1790
+
1791
+
1792
+ Соотношение 20:29:
1793
+
1794
+ 1280x1856
1795
+
1796
+
1797
+ Соотношение 9:13:
1798
+
1799
+ 576x832, 1152x1664
1800
+
1801
+
1802
+ Соотношение 16:23:
1803
+
1804
+ 1024x1472
1805
+
1806
+
1807
+ Соотношение 7:10:
1808
+
1809
+ 896x1280, 1344x1920
1810
+
1811
+
1812
+ Соотношение 19:27:
1813
+
1814
+ 1216x1728
1815
+
1816
+
1817
+ Соотношение 12:17:
1818
+
1819
+ 768x1088
1820
+
1821
+
1822
+ Соотношение 17:24:
1823
+
1824
+ 1088x1536
1825
+
1826
+
1827
+ Соотношение 22:31:
1828
+
1829
+ 1408x1984
1830
+
1831
+
1832
+ Соотношение 5:7:
1833
+
1834
+ 640x896, 960x1344, 1280x1792
1835
+
1836
+
1837
+ Соотношение 23:32:
1838
+
1839
+ 1472x2048
1840
+
1841
+
1842
+ Соотношение 18:25:
1843
+
1844
+ 1152x1600
1845
+
1846
+
1847
+ Соотношение 13:18:
1848
+
1849
+ 832x1152
1850
+
1851
+
1852
+ Соотношение 21:29:
1853
+
1854
+ 1344x1856
1855
+
1856
+
1857
+ Соотношение 8:11:
1858
+
1859
+ 512x704, 1024x1408
1860
+
1861
+
1862
+ Соотношение 19:26:
1863
+
1864
+ 1216x1664
1865
+
1866
+
1867
+ Соотношение 11:15:
1868
+
1869
+ 704x960, 1408x1920
1870
+
1871
+
1872
+ Соотношение 14:19:
1873
+
1874
+ 896x1216
1875
+
1876
+
1877
+ Соотношение 17:23:
1878
+
1879
+ 1088x1472
1880
+
1881
+
1882
+ Соотношение 20:27:
1883
+
1884
+ 1280x1728
1885
+
1886
+
1887
+ Соотношение 23:31:
1888
+
1889
+ 1472x1984
1890
+
1891
+
1892
+ Соотношение 3:4:
1893
+
1894
+ 576x768, 768x1024, 960x1280, 1152x1536, 1344x1792, 1536x2048
1895
+
1896
+
1897
+ Соотношение 22:29:
1898
+
1899
+ 1408x1856
1900
+
1901
+
1902
+ Соотношение 19:25:
1903
+
1904
+ 1216x1600
1905
+
1906
+
1907
+ Соотношение 16:21:
1908
+
1909
+ 1024x1344
1910
+
1911
+
1912
+ Соотношение 13:17:
1913
+
1914
+ 832x1088
1915
+
1916
+
1917
+ Соотношение 23:30:
1918
+
1919
+ 1472x1920
1920
+
1921
+
1922
+ Соотношение 10:13:
1923
+
1924
+ 640x832, 1280x1664
1925
+
1926
+
1927
+ Соотношение 17:22:
1928
+
1929
+ 1088x1408
1930
+
1931
+
1932
+ Соотношение 24:31:
1933
+
1934
+ 1536x1984
1935
+
1936
+
1937
+ Соотношение 7:9:
1938
+
1939
+ 896x1152, 1344x1728
1940
+
1941
+
1942
+ Соотношение 25:32:
1943
+
1944
+ 1600x2048
1945
+
1946
+
1947
+ Соотношение 18:23:
1948
+
1949
+ 1152x1472
1950
+
1951
+
1952
+ Соотношение 11:14:
1953
+
1954
+ 704x896, 1408x1792
1955
+
1956
+
1957
+ Соотношение 15:19:
1958
+
1959
+ 960x1216
1960
+
1961
+
1962
+ Соотношение 19:24:
1963
+
1964
+ 1216x1536
1965
+
1966
+
1967
+ Соотношение 23:29:
1968
+
1969
+ 1472x1856
1970
+
1971
+
1972
+ Соотношение 4:5:
1973
+
1974
+ 512x640, 768x960, 1024x1280, 1280x1600, 1536x1920
1975
+
1976
+
1977
+ Соотношение 25:31:
1978
+
1979
+ 1600x1984
1980
+
1981
+
1982
+ Соотношение 21:26:
1983
+
1984
+ 1344x1664
1985
+
1986
+
1987
+ Соотношение 17:21:
1988
+
1989
+ 1088x1344
1990
+
1991
+
1992
+ Соотношение 13:16:
1993
+
1994
+ 832x1024, 1664x2048
1995
+
1996
+
1997
+ Соотношение 22:27:
1998
+
1999
+ 1408x1728
2000
+
2001
+
2002
+ Соотношение 9:11:
2003
+
2004
+ 576x704, 1152x1408
2005
+
2006
+
2007
+ Соотношение 23:28:
2008
+
2009
+ 1472x1792
2010
+
2011
+
2012
+ Соотношение 14:17:
2013
+
2014
+ 896x1088
2015
+
2016
+
2017
+ Соотношение 19:23:
2018
+
2019
+ 1216x1472
2020
+
2021
+
2022
+ Соотношение 24:29:
2023
+
2024
+ 1536x1856
2025
+
2026
+
2027
+ Соотношение 5:6:
2028
+
2029
+ 640x768, 960x1152, 1280x1536, 1600x1920
2030
+
2031
+
2032
+ Соотношение 26:31:
2033
+
2034
+ 1664x1984
2035
+
2036
+
2037
+ Соотношение 21:25:
2038
+
2039
+ 1344x1600
2040
+
2041
+
2042
+ Соотношение 16:19:
2043
+
2044
+ 1024x1216
2045
+
2046
+
2047
+ Соотношение 27:32:
2048
+
2049
+ 1728x2048
2050
+
2051
+
2052
+ Соотношение 11:13:
2053
+
2054
+ 704x832, 1408x1664
2055
+
2056
+
2057
+ Соотношение 17:20:
2058
+
2059
+ 1088x1280
2060
+
2061
+
2062
+ Соотношение 23:27:
2063
+
2064
+ 1472x1728
2065
+
2066
+
2067
+ Соотношение 6:7:
2068
+
2069
+ 768x896, 1152x1344, 1536x1792
2070
+
2071
+
2072
+ Соотношение 25:29:
2073
+
2074
+ 1600x1856
2075
+
2076
+
2077
+ Соотношение 19:22:
2078
+
2079
+ 1216x1408
2080
+
2081
+
2082
+ Соотношение 13:15:
2083
+
2084
+ 832x960, 1664x1920
2085
+
2086
+
2087
+ Соотношение 20:23:
2088
+
2089
+ 1280x1472
2090
+
2091
+
2092
+ Соотношение 27:31:
2093
+
2094
+ 1728x1984
2095
+
2096
+
2097
+ Соотношение 7:8:
2098
+
2099
+ 896x1024, 1344x1536, 1792x2048
2100
+
2101
+
2102
+ Соотношение 22:25:
2103
+
2104
+ 1408x1600
2105
+
2106
+
2107
+ Соотношение 15:17:
2108
+
2109
+ 960x1088
2110
+
2111
+
2112
+ Соотношение 23:26:
2113
+
2114
+ 1472x1664
2115
+
2116
+
2117
+ Соотношение 8:9:
2118
+
2119
+ 512x576, 1024x1152, 1536x1728
2120
+
2121
+
2122
+ Соотношение 25:28:
2123
+
2124
+ 1600x1792
2125
+
2126
+
2127
+ Соотношение 17:19:
2128
+
2129
+ 1088x1216
2130
+
2131
+
2132
+ Соотношение 26:29:
2133
+
2134
+ 1664x1856
2135
+
2136
+
2137
+ Соотношение 9:10:
2138
+
2139
+ 576x640, 1152x1280, 1728x1920
2140
+
2141
+
2142
+ Соотношение 28:31:
2143
+
2144
+ 1792x1984
2145
+
2146
+
2147
+ Соотношение 19:21:
2148
+
2149
+ 1216x1344
2150
+
2151
+
2152
+ Соотношение 29:32:
2153
+
2154
+ 1856x2048
2155
+
2156
+
2157
+ Соотношение 10:11:
2158
+
2159
+ 640x704, 1280x1408
2160
+
2161
+
2162
+ Соотношение 21:23:
2163
+
2164
+ 1344x1472
2165
+
2166
+
2167
+ Соотношение 11:12:
2168
+
2169
+ 704x768, 1408x1536
2170
+
2171
+
2172
+ Соотношение 23:25:
2173
+
2174
+ 1472x1600
2175
+
2176
+
2177
+ Соотношение 12:13:
2178
+
2179
+ 768x832, 1536x1664
2180
+
2181
+
2182
+ Соотношение 25:27:
2183
+
2184
+ 1600x1728
2185
+
2186
+
2187
+ Соотношение 13:14:
2188
+
2189
+ 832x896, 1664x1792
2190
+
2191
+
2192
+ Соотношение 27:29:
2193
+
2194
+ 1728x1856
2195
+
2196
+
2197
+ Соотношение 14:15:
2198
+
2199
+ 896x960, 1792x1920
2200
+
2201
+
2202
+ Соотношение 29:31:
2203
+
2204
+ 1856x1984
2205
+
2206
+
2207
+ Соотношение 15:16:
2208
+
2209
+ 960x1024, 1920x2048
2210
+
2211
+
2212
+ Соотношение 16:17:
2213
+
2214
+ 1024x1088
2215
+
2216
+
2217
+ Соотношение 17:18:
2218
+
2219
+ 1088x1152
2220
+
2221
+
2222
+ Соотношение 18:19:
2223
+
2224
+ 1152x1216
2225
+
2226
+
2227
+ Соотношение 19:20:
2228
+
2229
+ 1216x1280
2230
+
2231
+
2232
+ Соотношение 20:21:
2233
+
2234
+ 1280x1344
2235
+
2236
+
2237
+ Соотношение 21:22:
2238
+
2239
+ 1344x1408
2240
+
2241
+
2242
+ Соотношение 22:23:
2243
+
2244
+ 1408x1472
2245
+
2246
+
2247
+ Соотношение 23:24:
2248
+
2249
+ 1472x1536
2250
+
2251
+
2252
+ Соотношение 24:25:
2253
+
2254
+ 1536x1600
2255
+
2256
+
2257
+ Соотношение 25:26:
2258
+
2259
+ 1600x1664
2260
+
2261
+
2262
+ Соотношение 26:27:
2263
+
2264
+ 1664x1728
2265
+
2266
+
2267
+ Соотношение 27:28:
2268
+
2269
+ 1728x1792
2270
+
2271
+
2272
+ Соотношение 28:29:
2273
+
2274
+ 1792x1856
2275
+
2276
+
2277
+ Соотношение 29:30:
2278
+
2279
+ 1856x1920
2280
+
2281
+
2282
+ Соотношение 30:31:
2283
+
2284
+ 1920x1984
2285
+
2286
+
2287
+ Соотношение 31:32:
2288
+
2289
+ 1984x2048