fix sorting
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user