Accept matrix generation in start request
Browse files
API.md
CHANGED
|
@@ -14,7 +14,7 @@ Returns `{"status":true}` when the application is ready.
|
|
| 14 |
|
| 15 |
`POST /start?p=PASSWORD` queues a matrix run and returns its reserved `Ngr.epng` path. See `send.py` for each request body.
|
| 16 |
|
| 17 |
-
Omitting `type` defaults to `checkpoint`, which runs the existing all-checkpoint matrix. `sampler` compares every sampler in a one-row grid, `scheduler` compares every scheduler in a one-row grid, and `sampler+scheduler` compares every combination. These comparison types resolve the numeric `model_1` and `model_2` prefixes to checkpoint names and use them for the first and second passes. Scheduler comparisons accept `sampler`; an empty value uses DPM++ 2M.
|
| 18 |
|
| 19 |
Each run uses fixed prompts, seeds, dimensions, steps, CFG, upscale settings, and denoise across its cells. The sampler comparison keeps Karras fixed. The scheduler comparison keeps its selected sampler fixed.
|
| 20 |
|
|
|
|
| 14 |
|
| 15 |
`POST /start?p=PASSWORD` queues a matrix run and returns its reserved `Ngr.epng` path. See `send.py` for each request body.
|
| 16 |
|
| 17 |
+
`generation` is required in `g{N}` form, sets the checkpoint image suffix, and uses `N` as the seed. Omitting `type` defaults to `checkpoint`, which runs the existing all-checkpoint matrix. `sampler` compares every sampler in a one-row grid, `scheduler` compares every scheduler in a one-row grid, and `sampler+scheduler` compares every combination. These comparison types resolve the numeric `model_1` and `model_2` prefixes to checkpoint names and use them for the first and second passes. Scheduler comparisons accept `sampler`; an empty value uses DPM++ 2M.
|
| 18 |
|
| 19 |
Each run uses fixed prompts, seeds, dimensions, steps, CFG, upscale settings, and denoise across its cells. The sampler comparison keeps Karras fixed. The scheduler comparison keeps its selected sampler fixed.
|
| 20 |
|
app.py
CHANGED
|
@@ -157,8 +157,6 @@ MATRIX_ROW_LABEL_WIDTH = 160
|
|
| 157 |
MATRIX_GRID_SCALE = .25
|
| 158 |
MATRIX_GRID_CELL_WIDTH = round(MATRIX_CELL_WIDTH * MATRIX_GRID_SCALE)
|
| 159 |
MATRIX_GRID_CELL_HEIGHT = round(MATRIX_CELL_HEIGHT * MATRIX_GRID_SCALE)
|
| 160 |
-
MATRIX_GENERATION = "g2"
|
| 161 |
-
MATRIX_SEED = int(MATRIX_GENERATION[1:])
|
| 162 |
MATRIX_TYPES = ("checkpoint", "sampler", "scheduler", "sampler+scheduler")
|
| 163 |
GPU_ATTEMPTS = 3
|
| 164 |
GPU_RETRY_ERRORS = (
|
|
@@ -287,6 +285,7 @@ class DownloadRequest(BaseModel):
|
|
| 287 |
class MatrixRequest(BaseModel):
|
| 288 |
model_config = ConfigDict(extra="forbid")
|
| 289 |
|
|
|
|
| 290 |
type: str = "checkpoint"
|
| 291 |
positive: str
|
| 292 |
negative: str
|
|
@@ -298,6 +297,7 @@ class MatrixRequest(BaseModel):
|
|
| 298 |
class MatrixCellRequest(BaseModel):
|
| 299 |
model_config = ConfigDict(extra="forbid")
|
| 300 |
|
|
|
|
| 301 |
positive: str
|
| 302 |
negative: str
|
| 303 |
folder: str
|
|
@@ -652,7 +652,7 @@ def generate_comparison_cell(request, output):
|
|
| 652 |
)[0]
|
| 653 |
samples = run_sampler(
|
| 654 |
first_model,
|
| 655 |
-
|
| 656 |
MATRIX_FIRST_STEPS,
|
| 657 |
MATRIX_FIRST_CFG,
|
| 658 |
request.sampler,
|
|
@@ -683,7 +683,7 @@ def generate_comparison_cell(request, output):
|
|
| 683 |
)[0]
|
| 684 |
samples = run_sampler(
|
| 685 |
second_model,
|
| 686 |
-
|
| 687 |
MATRIX_SECOND_STEPS,
|
| 688 |
MATRIX_SECOND_CFG,
|
| 689 |
request.sampler,
|
|
@@ -718,7 +718,12 @@ def generate_matrix_cell_core(body):
|
|
| 718 |
raise ValueError("Invalid matrix output")
|
| 719 |
return generate_comparison_cell(request, output)
|
| 720 |
|
| 721 |
-
diagonal = matrix_image_path(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 722 |
if first_id == second_id:
|
| 723 |
if diagonal.is_file():
|
| 724 |
return first_id
|
|
@@ -738,7 +743,7 @@ def generate_matrix_cell_core(body):
|
|
| 738 |
)[0]
|
| 739 |
samples = run_sampler(
|
| 740 |
model,
|
| 741 |
-
|
| 742 |
MATRIX_FIRST_STEPS,
|
| 743 |
MATRIX_FIRST_CFG,
|
| 744 |
MATRIX_SAMPLER,
|
|
@@ -755,7 +760,12 @@ def generate_matrix_cell_core(body):
|
|
| 755 |
save_named_image(tensor_image(image), diagonal)
|
| 756 |
return first_id
|
| 757 |
|
| 758 |
-
output = matrix_image_path(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 759 |
if output.is_file():
|
| 760 |
return f"{first_id}-{second_id}"
|
| 761 |
|
|
@@ -779,7 +789,7 @@ def generate_matrix_cell_core(body):
|
|
| 779 |
)[0]
|
| 780 |
samples = run_sampler(
|
| 781 |
model,
|
| 782 |
-
|
| 783 |
MATRIX_SECOND_STEPS,
|
| 784 |
MATRIX_SECOND_CFG,
|
| 785 |
MATRIX_SAMPLER,
|
|
@@ -1058,16 +1068,16 @@ def comparison_plan(request):
|
|
| 1058 |
return first, second, schedulers, samplers
|
| 1059 |
|
| 1060 |
|
| 1061 |
-
def matrix_image_path(folder, first_id, second_id):
|
| 1062 |
name = (
|
| 1063 |
-
f"{first_id}{
|
| 1064 |
if first_id == second_id
|
| 1065 |
-
else f"{first_id}x{second_id}{
|
| 1066 |
)
|
| 1067 |
return IMAGE_DIR / folder / name
|
| 1068 |
|
| 1069 |
|
| 1070 |
-
def create_matrix_grid(folder, number, models):
|
| 1071 |
count = len(models)
|
| 1072 |
width = MATRIX_LABEL_SIZE + MATRIX_CELL_WIDTH * count
|
| 1073 |
height = MATRIX_LABEL_SIZE + MATRIX_CELL_HEIGHT * count
|
|
@@ -1094,7 +1104,12 @@ def create_matrix_grid(folder, number, models):
|
|
| 1094 |
)
|
| 1095 |
|
| 1096 |
for column, (second_id, _) in enumerate(models):
|
| 1097 |
-
path = matrix_image_path(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1098 |
with Image.open(BytesIO(stored_bytes(path))) as source:
|
| 1099 |
image = source.convert("RGB")
|
| 1100 |
cell_x = MATRIX_LABEL_SIZE + column * MATRIX_CELL_WIDTH
|
|
@@ -1252,6 +1267,7 @@ def run_checkpoint_matrix(request, folder, number):
|
|
| 1252 |
for model in models:
|
| 1253 |
queued_matrix_cell(
|
| 1254 |
MatrixCellRequest(
|
|
|
|
| 1255 |
positive=request.positive,
|
| 1256 |
negative=request.negative,
|
| 1257 |
folder=folder,
|
|
@@ -1265,6 +1281,7 @@ def run_checkpoint_matrix(request, folder, number):
|
|
| 1265 |
continue
|
| 1266 |
queued_matrix_cell(
|
| 1267 |
MatrixCellRequest(
|
|
|
|
| 1268 |
positive=request.positive,
|
| 1269 |
negative=request.negative,
|
| 1270 |
folder=folder,
|
|
@@ -1272,7 +1289,7 @@ def run_checkpoint_matrix(request, folder, number):
|
|
| 1272 |
second=second,
|
| 1273 |
)
|
| 1274 |
)
|
| 1275 |
-
create_matrix_grid(folder, number, models)
|
| 1276 |
|
| 1277 |
|
| 1278 |
def run_comparison_matrix(request, folder, number):
|
|
@@ -1294,6 +1311,7 @@ def run_comparison_matrix(request, folder, number):
|
|
| 1294 |
).name
|
| 1295 |
queued_matrix_cell(
|
| 1296 |
MatrixCellRequest(
|
|
|
|
| 1297 |
positive=request.positive,
|
| 1298 |
negative=request.negative,
|
| 1299 |
folder=folder,
|
|
|
|
| 157 |
MATRIX_GRID_SCALE = .25
|
| 158 |
MATRIX_GRID_CELL_WIDTH = round(MATRIX_CELL_WIDTH * MATRIX_GRID_SCALE)
|
| 159 |
MATRIX_GRID_CELL_HEIGHT = round(MATRIX_CELL_HEIGHT * MATRIX_GRID_SCALE)
|
|
|
|
|
|
|
| 160 |
MATRIX_TYPES = ("checkpoint", "sampler", "scheduler", "sampler+scheduler")
|
| 161 |
GPU_ATTEMPTS = 3
|
| 162 |
GPU_RETRY_ERRORS = (
|
|
|
|
| 285 |
class MatrixRequest(BaseModel):
|
| 286 |
model_config = ConfigDict(extra="forbid")
|
| 287 |
|
| 288 |
+
generation: str = Field(pattern=r"^g\d+$")
|
| 289 |
type: str = "checkpoint"
|
| 290 |
positive: str
|
| 291 |
negative: str
|
|
|
|
| 297 |
class MatrixCellRequest(BaseModel):
|
| 298 |
model_config = ConfigDict(extra="forbid")
|
| 299 |
|
| 300 |
+
generation: str = Field(pattern=r"^g\d+$")
|
| 301 |
positive: str
|
| 302 |
negative: str
|
| 303 |
folder: str
|
|
|
|
| 652 |
)[0]
|
| 653 |
samples = run_sampler(
|
| 654 |
first_model,
|
| 655 |
+
int(request.generation[1:]),
|
| 656 |
MATRIX_FIRST_STEPS,
|
| 657 |
MATRIX_FIRST_CFG,
|
| 658 |
request.sampler,
|
|
|
|
| 683 |
)[0]
|
| 684 |
samples = run_sampler(
|
| 685 |
second_model,
|
| 686 |
+
int(request.generation[1:]),
|
| 687 |
MATRIX_SECOND_STEPS,
|
| 688 |
MATRIX_SECOND_CFG,
|
| 689 |
request.sampler,
|
|
|
|
| 718 |
raise ValueError("Invalid matrix output")
|
| 719 |
return generate_comparison_cell(request, output)
|
| 720 |
|
| 721 |
+
diagonal = matrix_image_path(
|
| 722 |
+
request.folder,
|
| 723 |
+
first_id,
|
| 724 |
+
first_id,
|
| 725 |
+
request.generation,
|
| 726 |
+
)
|
| 727 |
if first_id == second_id:
|
| 728 |
if diagonal.is_file():
|
| 729 |
return first_id
|
|
|
|
| 743 |
)[0]
|
| 744 |
samples = run_sampler(
|
| 745 |
model,
|
| 746 |
+
int(request.generation[1:]),
|
| 747 |
MATRIX_FIRST_STEPS,
|
| 748 |
MATRIX_FIRST_CFG,
|
| 749 |
MATRIX_SAMPLER,
|
|
|
|
| 760 |
save_named_image(tensor_image(image), diagonal)
|
| 761 |
return first_id
|
| 762 |
|
| 763 |
+
output = matrix_image_path(
|
| 764 |
+
request.folder,
|
| 765 |
+
first_id,
|
| 766 |
+
second_id,
|
| 767 |
+
request.generation,
|
| 768 |
+
)
|
| 769 |
if output.is_file():
|
| 770 |
return f"{first_id}-{second_id}"
|
| 771 |
|
|
|
|
| 789 |
)[0]
|
| 790 |
samples = run_sampler(
|
| 791 |
model,
|
| 792 |
+
int(request.generation[1:]),
|
| 793 |
MATRIX_SECOND_STEPS,
|
| 794 |
MATRIX_SECOND_CFG,
|
| 795 |
MATRIX_SAMPLER,
|
|
|
|
| 1068 |
return first, second, schedulers, samplers
|
| 1069 |
|
| 1070 |
|
| 1071 |
+
def matrix_image_path(folder, first_id, second_id, generation):
|
| 1072 |
name = (
|
| 1073 |
+
f"{first_id}{generation}.epng"
|
| 1074 |
if first_id == second_id
|
| 1075 |
+
else f"{first_id}x{second_id}{generation}.epng"
|
| 1076 |
)
|
| 1077 |
return IMAGE_DIR / folder / name
|
| 1078 |
|
| 1079 |
|
| 1080 |
+
def create_matrix_grid(folder, number, models, generation):
|
| 1081 |
count = len(models)
|
| 1082 |
width = MATRIX_LABEL_SIZE + MATRIX_CELL_WIDTH * count
|
| 1083 |
height = MATRIX_LABEL_SIZE + MATRIX_CELL_HEIGHT * count
|
|
|
|
| 1104 |
)
|
| 1105 |
|
| 1106 |
for column, (second_id, _) in enumerate(models):
|
| 1107 |
+
path = matrix_image_path(
|
| 1108 |
+
folder,
|
| 1109 |
+
model_id,
|
| 1110 |
+
second_id,
|
| 1111 |
+
generation,
|
| 1112 |
+
)
|
| 1113 |
with Image.open(BytesIO(stored_bytes(path))) as source:
|
| 1114 |
image = source.convert("RGB")
|
| 1115 |
cell_x = MATRIX_LABEL_SIZE + column * MATRIX_CELL_WIDTH
|
|
|
|
| 1267 |
for model in models:
|
| 1268 |
queued_matrix_cell(
|
| 1269 |
MatrixCellRequest(
|
| 1270 |
+
generation=request.generation,
|
| 1271 |
positive=request.positive,
|
| 1272 |
negative=request.negative,
|
| 1273 |
folder=folder,
|
|
|
|
| 1281 |
continue
|
| 1282 |
queued_matrix_cell(
|
| 1283 |
MatrixCellRequest(
|
| 1284 |
+
generation=request.generation,
|
| 1285 |
positive=request.positive,
|
| 1286 |
negative=request.negative,
|
| 1287 |
folder=folder,
|
|
|
|
| 1289 |
second=second,
|
| 1290 |
)
|
| 1291 |
)
|
| 1292 |
+
create_matrix_grid(folder, number, models, request.generation)
|
| 1293 |
|
| 1294 |
|
| 1295 |
def run_comparison_matrix(request, folder, number):
|
|
|
|
| 1311 |
).name
|
| 1312 |
queued_matrix_cell(
|
| 1313 |
MatrixCellRequest(
|
| 1314 |
+
generation=request.generation,
|
| 1315 |
positive=request.positive,
|
| 1316 |
negative=request.negative,
|
| 1317 |
folder=folder,
|
send.py
CHANGED
|
@@ -5,6 +5,7 @@ import urllib.request
|
|
| 5 |
BASE_URL = "https://q6-c.hf.space"
|
| 6 |
PASSWORD = "haip"
|
| 7 |
TYPE = "sampler"
|
|
|
|
| 8 |
POSITIVE = open('positive.txt', 'r').read()
|
| 9 |
NEGATIVE = open('neg.txt', 'r').read()
|
| 10 |
SAMPLER = ""
|
|
@@ -46,6 +47,7 @@ if TYPE == "sampler+scheduler":
|
|
| 46 |
"negative": NEGATIVE,
|
| 47 |
}
|
| 48 |
|
|
|
|
| 49 |
query = urllib.parse.urlencode({"p": PASSWORD})
|
| 50 |
request = urllib.request.Request(
|
| 51 |
f"{BASE_URL}/start?{query}",
|
|
|
|
| 5 |
BASE_URL = "https://q6-c.hf.space"
|
| 6 |
PASSWORD = "haip"
|
| 7 |
TYPE = "sampler"
|
| 8 |
+
GENERATION = "g2"
|
| 9 |
POSITIVE = open('positive.txt', 'r').read()
|
| 10 |
NEGATIVE = open('neg.txt', 'r').read()
|
| 11 |
SAMPLER = ""
|
|
|
|
| 47 |
"negative": NEGATIVE,
|
| 48 |
}
|
| 49 |
|
| 50 |
+
body["generation"] = GENERATION
|
| 51 |
query = urllib.parse.urlencode({"p": PASSWORD})
|
| 52 |
request = urllib.request.Request(
|
| 53 |
f"{BASE_URL}/start?{query}",
|