check_bounding_boxes.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. from dataclasses import dataclass
  2. import json
  3. import sys
  4. @dataclass
  5. class RectAndField:
  6. rect: list[float]
  7. rect_type: str
  8. field: dict
  9. def get_bounding_box_messages(fields_json_stream) -> list[str]:
  10. messages = []
  11. fields = json.load(fields_json_stream)
  12. messages.append(f"Read {len(fields['form_fields'])} fields")
  13. def rects_intersect(r1, r2):
  14. disjoint_horizontal = r1[0] >= r2[2] or r1[2] <= r2[0]
  15. disjoint_vertical = r1[1] >= r2[3] or r1[3] <= r2[1]
  16. return not (disjoint_horizontal or disjoint_vertical)
  17. rects_and_fields = []
  18. for f in fields["form_fields"]:
  19. rects_and_fields.append(RectAndField(f["label_bounding_box"], "label", f))
  20. rects_and_fields.append(RectAndField(f["entry_bounding_box"], "entry", f))
  21. has_error = False
  22. for i, ri in enumerate(rects_and_fields):
  23. for j in range(i + 1, len(rects_and_fields)):
  24. rj = rects_and_fields[j]
  25. if ri.field["page_number"] == rj.field["page_number"] and rects_intersect(ri.rect, rj.rect):
  26. has_error = True
  27. if ri.field is rj.field:
  28. messages.append(f"FAILURE: intersection between label and entry bounding boxes for `{ri.field['description']}` ({ri.rect}, {rj.rect})")
  29. else:
  30. messages.append(f"FAILURE: intersection between {ri.rect_type} bounding box for `{ri.field['description']}` ({ri.rect}) and {rj.rect_type} bounding box for `{rj.field['description']}` ({rj.rect})")
  31. if len(messages) >= 20:
  32. messages.append("Aborting further checks; fix bounding boxes and try again")
  33. return messages
  34. if ri.rect_type == "entry":
  35. if "entry_text" in ri.field:
  36. font_size = ri.field["entry_text"].get("font_size", 14)
  37. entry_height = ri.rect[3] - ri.rect[1]
  38. if entry_height < font_size:
  39. has_error = True
  40. messages.append(f"FAILURE: entry bounding box height ({entry_height}) for `{ri.field['description']}` is too short for the text content (font size: {font_size}). Increase the box height or decrease the font size.")
  41. if len(messages) >= 20:
  42. messages.append("Aborting further checks; fix bounding boxes and try again")
  43. return messages
  44. if not has_error:
  45. messages.append("SUCCESS: All bounding boxes are valid")
  46. return messages
  47. if __name__ == "__main__":
  48. if len(sys.argv) != 2:
  49. print("Usage: check_bounding_boxes.py [fields.json]")
  50. sys.exit(1)
  51. with open(sys.argv[1]) as f:
  52. messages = get_bounding_box_messages(f)
  53. for msg in messages:
  54. print(msg)