Automatic orientation issues detection and fix, experimental
This commit is contained in:
1 parent
78a7b7679c
commit
46845c4612
12 files changed
+683
-9
No files matched your search
@@ -17,6 +17,7 @@ from pydantic import BaseModel, Field, model_validator
|
||||
from copienator import configuration as config
|
||||
from copienator.gemini_config import fresh_seed, thinking_config
|
||||
from copienator.copy_errors import marked_copy_paths
|
||||
from copienator.orientation import record_detection, reports as orientation_reports
|
||||
from copienator import (
|
||||
CliError,
|
||||
EvaluationWorkspace,
|
||||
@@ -149,6 +150,10 @@ class BoxItem(BaseModel):
|
||||
label: str = Field(description="The label associated with the specific box")
|
||||
|
||||
class AnnotationData(BaseModel):
|
||||
upside_down_pages: typing.List[typing.Annotated[int, Field(strict=True, ge=1)]] = Field(
|
||||
default_factory=list,
|
||||
description='1-based column numbers in this image that appear upside down (180 degrees)',
|
||||
)
|
||||
error: typing.Literal["insufficient_crop"] | None = Field(
|
||||
default=None, description="Report insufficient_crop when the crop hides information needed to identify labels"
|
||||
)
|
||||
@@ -160,7 +165,7 @@ class AnnotationData(BaseModel):
|
||||
|
||||
@model_validator(mode="after")
|
||||
def require_success_payload(self):
|
||||
if self.error is None and not {"name", "list"} <= self.model_fields_set:
|
||||
if self.error is None and not self.upside_down_pages and not {"name", "list"} <= self.model_fields_set:
|
||||
raise ValueError("Successful detection must include name and list")
|
||||
return self
|
||||
|
||||
@@ -286,6 +291,12 @@ students may skip questions. On success, return error=null.
|
||||
text = text.replace("image of the left columns", "image of the full pages")
|
||||
text += "\nThis image now contains full pages, in the same reading order. Identify the labels using this expanded view."
|
||||
|
||||
text += ('\nReport upside-down handwriting in upside_down_pages, using 1-based '
|
||||
'column numbers in THIS image, left to right. Only report clear 180-degree '
|
||||
'orientation errors; blank pages are not evidence. If any are reported, '
|
||||
'you may return name="Unknown", list=[] and error=null. Orientation '
|
||||
'reporting takes priority over insufficient_crop.')
|
||||
|
||||
contents = [
|
||||
types.Content(
|
||||
role="user",
|
||||
@@ -514,6 +525,10 @@ def process_copy_group(
|
||||
config=request_config,
|
||||
)
|
||||
annotation = AnnotationData.model_validate_json(response.text)
|
||||
if record_detection(workspace, image_file, annotation.upside_down_pages):
|
||||
output_json.unlink(missing_ok=True)
|
||||
print(f'[{group_key}] Orientation à vérifier avant de valider les labels.')
|
||||
break
|
||||
if annotation.error == "insufficient_crop":
|
||||
print(f"[{group_key}] {image_file.name}: insufficient crop: "
|
||||
f"{annotation.error_reason or 'labels require a wider view'}")
|
||||
@@ -573,7 +588,7 @@ def process_copy_group(
|
||||
annotation.list = sort_boxes_for_image(
|
||||
workspace, image_file, annotation.list
|
||||
)
|
||||
atomic_write_json(output_json, annotation.model_dump(exclude={"error", "error_reason"}))
|
||||
atomic_write_json(output_json, annotation.model_dump(exclude={"error", "error_reason", "upside_down_pages"}))
|
||||
accumulated_labels.extend(box.label for box in annotation.list)
|
||||
generated += 1
|
||||
print(f"[{group_key}] Saved {image_file.name} after {time.monotonic() - started:.0f}s.")
|
||||
@@ -696,7 +711,13 @@ def run(
|
||||
print("Incomplete images (rerun label detection to resume):")
|
||||
for path in sorted(incomplete):
|
||||
print(f" {path.name}")
|
||||
return ExitCode.PARTIAL if warnings or incomplete else ExitCode.SUCCESS
|
||||
orientation = {name: data for name, data in orientation_reports(workspace).items()
|
||||
if Path(name).stem in groups}
|
||||
for name, data in orientation.items():
|
||||
pages = sorted({p for values in data.get('images', {}).values() for p in values})
|
||||
if pages:
|
||||
print(f'[Orientation] {name}: pages {pages}. Signalez la copie ou utilisez la correction automatique.')
|
||||
return ExitCode.PARTIAL if warnings or incomplete or orientation else ExitCode.SUCCESS
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
Reference in new issue
Block a user