Compare commits

..
2 Commits
Author SHA1 Message Date
sebastien 359c62004c Disable flash-lite for labels 2026-09-08 20:04:17 +02:00
sebastien 13a08f6cbd fix sorting 2026-09-08 17:00:18 +02:00
4 changed files with 115 additions and 18 deletions
+31
View File
@@ -340,6 +340,34 @@ def group_images(image_files: list[Path]) -> dict[str, list[Path]]:
return dict(groups)
def sort_boxes_for_image(
workspace: EvaluationWorkspace,
image_file: Path,
boxes: list[BoxItem],
) -> list[BoxItem]:
"""Sort boxes in the image's page-column reading order when schema exists."""
match = re.match(r"(.+)_(\d+)$", image_file.stem)
if not match:
return boxes
schema_path = workspace.cutleft_dir / f"{match.group(1)}_schema.json"
try:
schema = read_json(schema_path)
columns_per_file = schema["columns_per_file"]
column_count = int(columns_per_file[int(match.group(2)) - 1])
if column_count < 1:
return boxes
except (OSError, KeyError, IndexError, TypeError, ValueError):
return boxes
def position(item: BoxItem) -> tuple[int, int, int]:
ymin, xmin, _ymax, xmax = item.box_2d
center_x = (xmin + xmax) // 2
column = min(column_count - 1, max(0, center_x * column_count // 1000))
return column, ymin, xmin
return sorted(boxes, key=position)
def _existing_context(output_json: Path) -> list[str]:
try:
loaded = read_json(output_json)
@@ -452,6 +480,9 @@ def process_copy_group(
continue
annotation.name = "Unknown"
annotation.list = sort_boxes_for_image(
workspace, image_file, annotation.list
)
atomic_write_json(output_json, annotation.model_dump())
accumulated_labels.extend(box.label for box in annotation.list)
generated += 1
+3 -15
View File
@@ -333,28 +333,16 @@ class ImageViewer:
try:
current_data = read_json(self.current_json_path)
nb_pages = self.current_meta["schema"]["columns_per_file"][
self.current_meta["part"] - 1
]
original_items = current_data["list"]
ordered_items = sort_bounding_boxes(original_items, nb_pages)
if ordered_items != original_items:
current_data["list"] = ordered_items
atomic_write_json(self.current_json_path, current_data)
print(
f"Reordered labels by column in "
f"{self.current_json_path.name}."
)
items = current_data["list"]
# Perform the conversion now, post-edit
converted_items = convert_list(
ordered_items,
items,
self.current_meta["part"],
self.current_meta["schema"]
)
labels = normalized_labels(ordered_items)
labels = normalized_labels(items)
false_labels = [
label for label in labels if label not in self.valid_labels
]
+4 -3
View File
@@ -19,13 +19,14 @@ FINAL_SCORE_FONT_PATH = None
MODEL_LITE_ID = "gemini-3.5-flash-lite"
# Modèle pour identifier visuellement les labels
MODEL_FOR_LABEL_ID = "gemini-3.5-flash-lite"
# MODEL_FOR_LABEL_ID = "gemini-3.5-flash-lite" # 3.5 flash lite marche moyennement, il insiste pour mettre les labels dans l'ordre, sans considérer ce qu'il y a écrit
MODEL_FOR_LABEL_ID = "gemini-3.8-flash"
# Modèle pour des choses normales
MODEL_FLASH_ID = "gemini-3.6-flash"
MODEL_FLASH_ID = "gemini-3.8-flash"
# Modèle pour des choses dures
MODEL_PRO_ID = "gemini-3.6-flash"
MODEL_PRO_ID = "gemini-3.8-flash"
# MODEL_PRO_ID = "gemini-3.1-pro-preview"
PAGE_SPLITTER_KB = {
+77
View File
@@ -910,6 +910,39 @@ class StandardCliTests(unittest.TestCase):
)
self.assertIsNone(viewer.accumulated_results)
def test_plotting_validation_preserves_manually_edited_order(self) -> None:
module = self.modules["plotting"]
with tempfile.TemporaryDirectory() as directory:
json_path = Path(directory) / "Copie01_01.json"
manually_ordered = [
{"box_2d": [100, 600, 130, 700], "label": "Ex 2"},
{"box_2d": [500, 100, 530, 200], "label": "Ex 1"},
]
atomic_write_json(
json_path,
{"name": "Student", "list": manually_ordered},
)
viewer = module.ImageViewer.__new__(module.ImageViewer)
viewer.is_viewing = True
viewer.current_json_path = json_path
viewer.current_meta = {
"schema": {"columns_per_file": [2]},
"part": 1,
}
viewer.valid_labels = {"Ex 1", "Ex 2"}
viewer.accumulated_results = {"name": "Student", "list": []}
viewer.history = []
viewer.current_pil_image = Mock()
viewer.label = Mock()
viewer.on_enter(None)
self.assertEqual(read_json(json_path)["list"], manually_ordered)
self.assertEqual(
[item["label"] for item in viewer.accumulated_results["list"]],
["Ex 2", "Ex 1"],
)
def test_plotting_validates_plain_and_directional_labels(self) -> None:
module = self.modules["plotting"]
self.assertEqual(
@@ -1008,6 +1041,50 @@ class StandardCliTests(unittest.TestCase):
},
)
def test_label_detection_writes_boxes_in_column_reading_order(self) -> None:
module = self.modules["gemini_for_labels"]
with tempfile.TemporaryDirectory() as directory:
evaluation = Path(directory) / "Exam"
copies = evaluation / "Copies"
cutleft = evaluation / "Cutleft"
copies.mkdir(parents=True)
cutleft.mkdir()
image = cutleft / "Copie03_02.jpg"
image.write_bytes(b"image")
atomic_write_json(
cutleft / "Copie03_schema.json",
{"columns_per_file": [2, 2]},
)
client = Mock()
client.models.generate_content.return_value = Mock(
text=(
'{"name":"Continued","list":['
'{"box_2d":[100,600,130,700],"label":"Ex 2"},'
'{"box_2d":[500,100,530,200],"label":"Ex 1"},'
'{"box_2d":[300,620,330,720],"label":"Ex 3"}]}'
)
)
module.process_copy_group(
EvaluationWorkspace(evaluation),
"Copie03",
[image],
client=client,
labels_text="Ex 1\nEx 2\nEx 3\n",
names_text="Student\n",
valid_labels={"Ex 1", "Ex 2", "Ex 3"},
valid_names={"Student", "Unknown", "Continued"},
overwrite=True,
sleep=lambda _seconds: None,
target_interval=0,
)
result = read_json(copies / "Copie03_02.json")
self.assertEqual(
[item["label"] for item in result["list"]],
["Ex 1", "Ex 2", "Ex 3"],
)
def test_label_detection_retries_unknown_labels(self) -> None:
module = self.modules["gemini_for_labels"]
with tempfile.TemporaryDirectory() as directory: