document_redaction / test /test_annotator_box_collapse_guard.py
seanpedrickcase's picture
Sync: redaction boxes should now remain visible on page changes
54b0fbc
Raw
History Blame Contribute Delete
5.67 kB
"""Regression: collapsed/empty annotator boxes must not wipe page redactions."""
from __future__ import annotations
import os
os.environ.setdefault("PYTHONUTF8", "1")
import pytest
pytest.importorskip("gradio_image_annotation_redaction")
from tools.redaction_review import (
_annotator_box_has_area,
_annotator_boxes_are_collapsed,
update_all_page_annotation_object_based_on_previous_page,
)
def test_annotator_box_has_area():
assert _annotator_box_has_area({"xmin": 0.1, "ymin": 0.2, "xmax": 0.3, "ymax": 0.4})
assert not _annotator_box_has_area(
{"xmin": 0.0, "ymin": 0.0, "xmax": 0.0, "ymax": 0.0}
)
assert not _annotator_box_has_area({"xmin": 10, "ymin": 20, "xmax": 10, "ymax": 25})
def test_annotator_boxes_are_collapsed_includes_empty():
# Empty and all-zero payloads are both unsafe to write over existing boxes
# (Gradio often returns [] briefly on page turns).
assert _annotator_boxes_are_collapsed(None) is True
assert _annotator_boxes_are_collapsed([]) is True
assert (
_annotator_boxes_are_collapsed([{"xmin": 0, "ymin": 0, "xmax": 0, "ymax": 0}])
is True
)
assert (
_annotator_boxes_are_collapsed(
[
{"xmin": 0, "ymin": 0, "xmax": 0, "ymax": 0},
{"xmin": 0.1, "ymin": 0.2, "xmax": 0.3, "ymax": 0.4},
]
)
is False
)
def test_update_all_page_keeps_existing_when_incoming_boxes_collapsed():
existing = {
"image": "page_0.png",
"boxes": [
{
"xmin": 0.1,
"ymin": 0.2,
"xmax": 0.3,
"ymax": 0.4,
"label": "PERSON",
"id": "abc",
}
],
}
collapsed = {
"image": "page_0.png",
"boxes": [
{
"xmin": 0,
"ymin": 0,
"xmax": 0,
"ymax": 0,
"label": "PERSON",
"id": "abc",
}
],
"orientation": 0,
}
page_sizes = [
{
"page": 1,
"image_path": "page_0.png",
"image_width": 100,
"image_height": 200,
}
]
updated, current, bottom = update_all_page_annotation_object_based_on_previous_page(
collapsed,
current_page=1,
previous_page=1,
all_image_annotations=[existing],
page_sizes=page_sizes,
)
assert current == 1
assert bottom == 1
assert len(updated[0]["boxes"]) == 1
assert updated[0]["boxes"][0]["xmin"] == 0.1
assert updated[0]["boxes"][0]["ymax"] == 0.4
def test_update_all_page_keeps_existing_when_incoming_boxes_empty():
existing = {
"image": "page_0.png",
"boxes": [
{
"xmin": 0.1,
"ymin": 0.2,
"xmax": 0.3,
"ymax": 0.4,
"label": "PERSON",
"id": "abc",
}
],
}
cleared = {"image": "page_0.png", "boxes": [], "orientation": 0}
page_sizes = [
{
"page": 1,
"image_path": "page_0.png",
"image_width": 100,
"image_height": 200,
},
{
"page": 2,
"image_path": "page_1.png",
"image_width": 100,
"image_height": 200,
},
]
updated, _, _ = update_all_page_annotation_object_based_on_previous_page(
cleared,
current_page=2,
previous_page=1,
all_image_annotations=[existing, {"image": "page_1.png", "boxes": []}],
page_sizes=page_sizes,
)
assert len(updated[0]["boxes"]) == 1
assert updated[0]["boxes"][0]["id"] == "abc"
def test_update_all_page_previous_page_zero_does_not_overwrite_last_page():
state = [
{
"image": "doc_0.png",
"boxes": [
{
"xmin": 0.1,
"ymin": 0.2,
"xmax": 0.3,
"ymax": 0.4,
"label": "PERSON",
"id": "a",
}
],
},
{
"image": "doc_1.png",
"boxes": [
{
"xmin": 0.2,
"ymin": 0.3,
"xmax": 0.4,
"ymax": 0.5,
"label": "PERSON",
"id": "c",
}
],
},
]
annotator = {
"image": "doc_0.png",
"boxes": [
{
"xmin": 100,
"ymin": 280,
"xmax": 300,
"ymax": 560,
"label": "PERSON",
"id": "a",
}
],
"orientation": 0,
}
page_sizes = [
{
"page": 1,
"image_path": "doc_0.png",
"image_width": 1000,
"image_height": 1400,
},
{
"page": 2,
"image_path": "doc_1.png",
"image_width": 1000,
"image_height": 1400,
},
]
updated, _, _ = update_all_page_annotation_object_based_on_previous_page(
annotator,
current_page=2,
previous_page=0, # initial State used to be 0
all_image_annotations=state,
page_sizes=page_sizes,
)
# previous_page=0 must coerce to page 1, not index -1 (last page)
assert updated[1]["boxes"][0]["id"] == "c"
assert updated[0]["boxes"][0]["id"] == "a"
assert updated[0]["boxes"][0]["xmin"] == pytest.approx(0.1)