Compare commits

...

49 Commits

Author SHA1 Message Date
Jonathan Kössler
799fe331c3 Merge branch 'bugfix/RED-10722' into 'master'
RED-10722: fix dead letter queue

Closes RED-10722

See merge request redactmanager/cv-analysis-service!32
2025-01-16 09:28:41 +01:00
Jonathan Kössler
dfbfc50556 chore: update version 2025-01-15 13:40:01 +01:00
Jonathan Kössler
63fbd387a3 chore: update pyinfra to v3.4.2 2025-01-15 13:32:57 +01:00
Jonathan Kössler
41dbfc69d9 chore: update pyinfra to v3.4.2 2025-01-14 16:52:13 +01:00
Jonathan Kössler
b73e9b2ed9 Merge branch 'feature/RED-10441' into 'master'
RED-10441: fix abandoned queues

Closes RED-10441

See merge request redactmanager/cv-analysis-service!31
2024-11-13 17:27:22 +01:00
Jonathan Kössler
92692281ce chore: update pyinfra to v3.3.5 2024-11-13 17:22:24 +01:00
Jonathan Kössler
cb0c58d699 chore: update pyinfra to v3.3.4 2024-11-13 16:41:04 +01:00
Jonathan Kössler
eb96403fe2 chore: update pyinfra to v3.3.3 2024-11-13 14:53:11 +01:00
Jonathan Kössler
c8daf888c6 chore: update pyinfra to v3.3.2 2024-11-13 09:45:43 +01:00
Jonathan Kössler
eb921c365d Merge branch 'chore/update_pyinfra' into 'master'
RES-858: fix graceful shutdown

See merge request redactmanager/cv-analysis-service!30
2024-09-30 11:01:07 +02:00
Jonathan Kössler
7762f81a4a chore: update pyinfra to v3.2.11 2024-09-30 10:07:29 +02:00
Jonathan Kössler
e991cfe1bf Merge branch 'chore/update_pyinfra' into 'master'
RES-844 && RES-856: fix tracing & proto format

See merge request redactmanager/cv-analysis-service!29
2024-09-27 08:21:46 +02:00
Jonathan Kössler
35c5ee5831 fix: opentelemtry service name 2024-09-26 13:46:05 +02:00
Jonathan Kössler
e97f34391a chore: update pyinfra to v3.2.10 2024-09-26 13:44:48 +02:00
Francisco Schulz
1fa10721aa Merge branch 'RED-10017-investigate-crashing-py-services-when-upload-large-number-of-files' into 'master'
RED-10017 "Investigate crashing py services when upload large number of files"

See merge request redactmanager/cv-analysis-service!28
2024-09-23 18:55:08 +02:00
Francisco Schulz
7f0d0a48db RED-10017 "Investigate crashing py services when upload large number of files" 2024-09-23 18:55:08 +02:00
Francisco Schulz
333cd498b9 Merge branch 'RES-842-pyinfra-fix-rabbit-mq-handler-shuts-down-when-queues-not-available-yet' into 'master'
chore: update pyinfra version, increase pkg version

Closes RES-842

See merge request redactmanager/cv-analysis-service!27
2024-08-30 14:57:14 +02:00
francisco.schulz
9df8c8f936 chore: update service version 2024-08-30 08:25:00 -04:00
francisco.schulz
60adf0c381 chore: update pyinfra version 2024-08-30 08:15:34 -04:00
francisco.schulz
537f605a85 chore: remove renovate bot config 2024-08-29 11:48:15 -04:00
francisco.schulz
66987ab8e9 chore: update pyinfra version, increase pkg version 2024-08-29 11:29:07 -04:00
Jonathan Kössler
43570142c3 Merge branch 'feature/RES-840-add-client-connector-error' into 'master'
fix: add exception handling for ClientConnectorError

Closes RES-840

See merge request redactmanager/cv-analysis-service!26
2024-08-28 15:47:01 +02:00
Jonathan Kössler
d457f49001 chore: update pyinfra version 2024-08-28 14:47:29 +02:00
Jonathan Kössler
536928c032 Merge branch 'feature/RES-826-pyinfra-update' into 'master'
chore: bump pyinfra version

Closes RES-826

See merge request redactmanager/cv-analysis-service!25
2024-08-26 16:15:17 +02:00
Jonathan Kössler
dc6183490f chore: bump pyinfra version 2024-08-26 15:13:59 +02:00
Jonathan Kössler
bbc2d0c8bf chore: bump pyinfra version 2024-08-22 09:33:26 +02:00
Jonathan Kössler
3462faf8c7 Merge branch 'feature/RES-731-add-queues-per-tenant' into 'master'
RES-731: add queues per tenant

Closes RES-731

See merge request redactmanager/cv-analysis-service!24
2024-08-19 15:03:38 +02:00
Jonathan Kössler
b136cc9ff3 RES-731: add queues per tenant 2024-08-19 15:03:37 +02:00
Julius Unverfehrt
cf431df1cb Merge branch 'table_lines' into 'master'
Table lines

See merge request redactmanager/cv-analysis-service!23
2024-05-15 16:53:27 +02:00
iriley
23406004ed chore: remove debug code 2024-05-15 16:38:51 +02:00
iriley
b0467f2335 chore: debug coordinate remapping logic, esp mirroring 2024-05-15 16:33:58 +02:00
Julius Unverfehrt
e86214f6b7 Merge branch 'table_lines' into 'master'
fix: maping of image coordinates to pdf coordinates (table inference)

See merge request redactmanager/cv-analysis-service!22
2024-05-15 13:02:24 +02:00
iriley
3b8d6eda04 fix: maping of image coordinates to pdf coordinates (table inference) 2024-05-15 11:48:31 +02:00
Isaac Riley
3c9ddfcf0f Merge branch 'table_lines' into 'master'
fix: check nonzero list length in filter_fp_col_lines

See merge request redactmanager/cv-analysis-service!21
2024-05-13 09:39:20 +02:00
iriley
b854312b08 fix: check nonzero list length in filter_fp_col_lines 2024-05-13 08:40:31 +02:00
Isaac Riley
0f45a25bc8 Merge branch 'table_lines' into 'master'
fix: make envvar conditional unfailable

See merge request redactmanager/cv-analysis-service!20
2024-05-08 15:33:45 +02:00
iriley
8762363aa9 Merge branch 'table_lines' 2024-05-08 15:20:51 +02:00
iriley
72d26c4712 fix: make envvar conditional unfailable 2024-05-08 13:44:11 +02:00
Francisco Schulz
62fb637978 fix: generate docs when merging into master branch 2024-05-07 14:17:19 +02:00
Isaac Riley
802372a504 Update .gitlab-ci.yml file to build docs in build job rather than unit-tests 2024-05-07 12:41:46 +02:00
Isaac Riley
ceb1c00784 Update .gitlab-ci.yml file 2024-05-07 12:27:47 +02:00
Isaac Riley
f1f9e8d2bc Update .gitlab-ci.yml file 2024-05-07 10:59:21 +02:00
Isaac Riley
8fcb6f29fb Update .gitlab-ci.yml 2024-05-07 10:40:01 +02:00
Isaac Riley
79926b9990 Update .gitlab-ci.yml file 2024-05-07 09:07:00 +02:00
Isaac Riley
6d37622e95 Remove error in .gitlab-ci.yml file 2024-05-07 07:47:58 +02:00
Isaac Riley
6341512250 Define pages job in .gitlab-ci.yml file 2024-05-07 07:42:16 +02:00
Isaac Riley
713697b32d Update .gitlab-ci.yml file 2024-05-07 07:40:52 +02:00
Isaac Riley
b6e2540399 Update .gitlab-ci.yml 2024-05-07 07:31:41 +02:00
Isaac Riley
78b8f18865 Update .gitlab-ci.yaml 2024-05-07 07:25:53 +02:00
20 changed files with 102281 additions and 2485 deletions

2
.gitignore vendored
View File

@ -49,4 +49,4 @@ __pycache__/
!drivers !drivers
# unignore files # unignore files
!bom.* !bom.*

View File

@ -1,28 +1,31 @@
# CI for services, check gitlab repo for python package CI
include: include:
- project: "Gitlab/gitlab" - project: "Gitlab/gitlab"
file: "/ci-templates/research/dvc.gitlab-ci.yml" ref: main
- project: "Gitlab/gitlab"
file: "/ci-templates/research/versioning-build-test-release.gitlab-ci.yml" file: "/ci-templates/research/versioning-build-test-release.gitlab-ci.yml"
# file: "/ci-templates/research/versioning-build+azure_model-test-release.gitlab-ci.yml" - project: "Gitlab/gitlab"
ref: main
file: "/ci-templates/research/docs.gitlab-ci.yml"
# set project variables here
variables: variables:
NEXUS_PROJECT_DIR: red NEXUS_PROJECT_DIR: red # subfolder in Nexus docker-gin where your container will be stored
IMAGENAME: "${CI_PROJECT_NAME}" IMAGENAME: $CI_PROJECT_NAME # if the project URL is gitlab.example.com/group-name/project-1, CI_PROJECT_NAME is project-1
stages: pages:
- data only:
- setup - master # KEEP THIS, necessary because `master` branch and not `main` branch
- unit-tests
- versioning
- build
- integration-tests
- release
################### ###################
# INTEGRATION TESTS # INTEGRATION TESTS
trigger-integration-tests: trigger-integration-tests:
extends: .integration-tests
# ADD THE MODEL BUILD WHICH SHOULD TRIGGER THE INTEGRATION TESTS
# needs:
# - job: docker-build::model_name
# artifacts: true
rules: rules:
- when: never # temp. disable integration tests - when: never
######### #########
# RELEASE # RELEASE
@ -30,5 +33,3 @@ release:
extends: .release extends: .release
needs: needs:
- !reference [.needs-versioning, needs] # leave this line as is - !reference [.needs-versioning, needs] # leave this line as is
- job: dvc-pull
artifacts: true

View File

@ -13,9 +13,7 @@ logger.add(sys.stdout, level="INFO")
def bashcmd(cmds: list) -> str: def bashcmd(cmds: list) -> str:
try: try:
logger.debug(f"running: {' '.join(cmds)}") logger.debug(f"running: {' '.join(cmds)}")
return subprocess.run( return subprocess.run(cmds, check=True, capture_output=True, text=True).stdout.strip("\n")
cmds, check=True, capture_output=True, text=True
).stdout.strip("\n")
except: except:
logger.warning(f"Error executing the following bash command: {' '.join(cmds)}.") logger.warning(f"Error executing the following bash command: {' '.join(cmds)}.")
raise raise

View File

@ -5,7 +5,7 @@ default_language_version:
python: python3.10 python: python3.10
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
@ -34,7 +34,7 @@ repos:
- --profile black - --profile black
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.3.0 rev: 24.10.0
hooks: hooks:
- id: black - id: black
# exclude: ^(docs/|notebooks/|data/|src/secrets/) # exclude: ^(docs/|notebooks/|data/|src/secrets/)
@ -42,7 +42,7 @@ repos:
- --line-length=120 - --line-length=120
- repo: https://github.com/compilerla/conventional-pre-commit - repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.2.0 rev: v4.0.0
hooks: hooks:
- id: conventional-pre-commit - id: conventional-pre-commit
pass_filenames: false pass_filenames: false

View File

@ -1,11 +1,22 @@
[asyncio]
max_concurrent_tasks = 10
[dynamic_tenant_queues]
enabled = true
[metrics.prometheus] [metrics.prometheus]
enabled = true enabled = true
prefix = "redactmanager_cv_analysis_service" prefix = "redactmanager_cv_analysis_service"
[tracing.opentelemetry] [tracing]
enabled = true enabled = true
# possible values "opentelemetry" | "azure_monitor" (Excpects APPLICATIONINSIGHTS_CONNECTION_STRING environment variable.)
type = "azure_monitor"
[tracing.opentelemetry]
endpoint = "http://otel-collector-opentelemetry-collector.otel-collector:4318/v1/traces" endpoint = "http://otel-collector-opentelemetry-collector.otel-collector:4318/v1/traces"
service_name = "redactmanager_cv_analyisis_service" service_name = "redactmanager_cv_analysis_service"
exporter = "otlp" exporter = "otlp"
[webserver] [webserver]
@ -25,6 +36,15 @@ input_queue = "request_queue"
output_queue = "response_queue" output_queue = "response_queue"
dead_letter_queue = "dead_letter_queue" dead_letter_queue = "dead_letter_queue"
tenant_event_queue_suffix = "_tenant_event_queue"
tenant_event_dlq_suffix = "_tenant_events_dlq"
tenant_exchange_name = "tenants-exchange"
queue_expiration_time = 300000 # 5 minutes in milliseconds
service_request_queue_prefix = "cv_analysis_request_queue"
service_request_exchange_name = "cv_analysis_request_exchange"
service_response_exchange_name = "cv_analysis_response_exchange"
service_dlq_name = "cv_analysis_dlq"
[storage] [storage]
backend = "s3" backend = "s3"
@ -41,4 +61,7 @@ connection_string = ""
[storage.tenant_server] [storage.tenant_server]
public_key = "" public_key = ""
endpoint = "http://tenant-user-management:8081/internal-api/tenants" endpoint = "http://tenant-user-management:8081/internal-api/tenants"
[kubernetes]
pod_name = "test_pod"

BIN
data/2017-1078223.pdf Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

8
flake.lock generated
View File

@ -20,17 +20,17 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1711703276, "lastModified": 1715155958,
"narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", "narHash": "sha256-I/V8oiPfK0KIQUc+3sAQLJJYa7L3edd9gdnKP2XvT7E=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", "rev": "240b1d794bbfca3522dec880a3aa300932bbfd98",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "240b1d794bbfca3522dec880a3aa300932bbfd98",
"type": "github" "type": "github"
} }
}, },

View File

@ -2,7 +2,7 @@
description = "An flake to use a Python poetry project in an FHS environment when poetry2nix is uncooperative"; description = "An flake to use a Python poetry project in an FHS environment when poetry2nix is uncooperative";
inputs = { inputs = {
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/240b1d794bbfca3522dec880a3aa300932bbfd98";
}; };
outputs = { outputs = {
self, self,

5723
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "cv-analysis-service" name = "cv-analysis-service"
version = "2.6.2" version = "2.30.0"
description = "" description = ""
authors = ["Isaac Riley <isaac.riley@knecon.com>"] authors = ["Isaac Riley <isaac.riley@knecon.com>"]
readme = "README.md" readme = "README.md"
@ -25,8 +25,8 @@ coverage = "^5.5"
dependency-check = "^0.6.0" dependency-check = "^0.6.0"
lorem-text = "^2.1" lorem-text = "^2.1"
PyMuPDF = "^1.19.6" PyMuPDF = "^1.19.6"
pyinfra = { version = "^2.2.0", source = "gitlab-research" } pyinfra = { version = "3.4.2", source = "gitlab-research" }
kn-utils = { version = "0.2.7", source = "gitlab-research" } kn-utils = { version = ">=0.4.0", source = "gitlab-research" }
pdf2img = { version = "0.7.0", source = "gitlab-red" } pdf2img = { version = "0.7.0", source = "gitlab-red" }
dvc-azure = "^2.21.2" dvc-azure = "^2.21.2"
pymupdf = "^1.24.1" pymupdf = "^1.24.1"
@ -76,7 +76,7 @@ priority = "explicit"
[tool.pylint] [tool.pylint]
max-line-length = 120 max-line-length = 120
docstring-min-length=4 docstring-min-length = 4
extension-pkg-whitelist = ["cv2"] extension-pkg-whitelist = ["cv2"]
extension-pkg-allow-list = ["cv2"] extension-pkg-allow-list = ["cv2"]

View File

@ -1,6 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}

41
scripts/devenvsetup.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/bash
python_version=$1
gitlab_user=$2
gitlab_personal_access_token=$3
# cookiecutter https://gitlab.knecon.com/knecon/research/template-python-project.git --checkout master
# latest_dir=$(ls -td -- */ | head -n 1) # should be the dir cookiecutter just created
# cd $latest_dir
pyenv install $python_version
pyenv local $python_version
pyenv shell $python_version
# install poetry globally (PREFERRED), only need to install it once
# curl -sSL https://install.python-poetry.org | python3 -
# remember to update poetry once in a while
poetry self update
# install poetry in current python environment, can lead to multiple instances of poetry being installed on one system (DISPREFERRED)
# pip install --upgrade pip
# pip install poetry
poetry config virtualenvs.in-project true
poetry config installer.max-workers 10
poetry config repositories.gitlab-research https://gitlab.knecon.com/api/v4/groups/19/-/packages/pypi
poetry config http-basic.gitlab-research ${gitlab_user} ${gitlab_personal_access_token}
poetry config repositories.gitlab-red https://gitlab.knecon.com/api/v4/groups/12/-/packages/pypi
poetry config http-basic.gitlab-red ${gitlab_user} ${gitlab_personal_access_token}
poetry config repositories.gitlab-fforesight https://gitlab.knecon.com/api/v4/groups/269/-/packages/pypi
poetry config http-basic.gitlab-fforesight ${gitlab_user} ${gitlab_personal_access_token}
poetry env use $(pyenv which python)
poetry install --with=dev
poetry update
source .venv/bin/activate
pre-commit install
pre-commit autoupdate

View File

@ -26,4 +26,5 @@ best_result = list(pipe(data={"pdf": pdf_bytes, "vlp_output": vlp_output}))
# print(best_result) # print(best_result)
annotate_pdf(pdf_bytes, best_result, output_path=args.output) if __name__ == "__main__":
annotate_pdf(pdf_bytes, best_result, output_path=args.output)

View File

@ -135,9 +135,11 @@ FILTERS: dict[str, dict[int, np.ndarray | None]] = {
def filter_fp_col_lines(line_list: list[int], filt_sums: Array) -> list[int]: def filter_fp_col_lines(line_list: list[int], filt_sums: Array) -> list[int]:
if not line_list: if not list(line_list):
return [] return []
centers = list(np.where((filt_sums[1:-1] < filt_sums[:-2]) * (filt_sums[1:-1] < filt_sums[2:]))[0] + 1) centers = list(np.where((filt_sums[1:-1] < filt_sums[:-2]) * (filt_sums[1:-1] < filt_sums[2:]))[0] + 1)
if not centers:
return []
if line_list[0] > centers[0]: if line_list[0] > centers[0]:
centers = centers[1:] + [len(filt_sums) - 1] centers = centers[1:] + [len(filt_sums) - 1]

View File

@ -4,7 +4,9 @@ from pathlib import Path
from typing import Union from typing import Union
import fitz # type: ignore import fitz # type: ignore
from kn_utils.logging import logger # type: ignore from kn_utils.logging import logger
from cv_analysis.utils.image_extraction import mirror_horizontal # type: ignore
def annotate_pdf( def annotate_pdf(
@ -27,8 +29,9 @@ def annotate_page(page: fitz.Page, prediction):
bbox = itemgetter("x1", "y1", "x2", "y2")(box["box"]) bbox = itemgetter("x1", "y1", "x2", "y2")(box["box"])
label, probability, uuid = itemgetter("label", "probability", "uuid")(box) label, probability, uuid = itemgetter("label", "probability", "uuid")(box)
bbox = mirror_on_x_axis(bbox, page.bound().height)
x0, y0, x1, y1 = bbox x0, y0, x1, y1 = bbox
print(page.bound)
page.draw_rect(fitz.Rect(x0, y0, x1, y1), color=(0, 0, 1), width=2) page.draw_rect(fitz.Rect(x0, y0, x1, y1), color=(0, 0, 1), width=2)
label_x, label_y = x0, y0 - 5 label_x, label_y = x0, y0 - 5
page.insert_text( page.insert_text(
@ -40,18 +43,18 @@ def annotate_page(page: fitz.Page, prediction):
for line in prediction.get("tableLines", []): for line in prediction.get("tableLines", []):
start = itemgetter("x1", "y1")(line) start = itemgetter("x1", "y1")(line)
end = itemgetter("x2", "y2")(line) end = itemgetter("x2", "y2")(line)
bbox = (*start, *end)
height = page.bound()[3]
bbox = mirror_horizontal(bbox, page_height=height)
start = tuple(bbox[:2])
end = tuple(bbox[2:])
page.draw_line(start, end, color=(1, 0, 0.5), width=1) page.draw_line(start, end, color=(1, 0, 0.5), width=1)
return page return page
def mirror_on_x_axis(bbox, page_height):
x0, y0, x1, y1 = bbox
y0_new = page_height - y1
y1_new = page_height - y0
return x0, y0_new, x1, y1_new
@singledispatch @singledispatch
def provide_byte_stream(pdf: Union[bytes, Path, str]) -> None: def provide_byte_stream(pdf: Union[bytes, Path, str]) -> None:
pass pass

View File

@ -3,10 +3,10 @@ import os
import cv2 import cv2
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
if os.environ["USER"] == "isaac": # if os.environ.get("USER") == "isaac":
import matplotlib # import matplotlib
matplotlib.use("module://matplotlib-backend-wezterm") # matplotlib.use("module://matplotlib-backend-wezterm")
def show_image_cv2(image, maxdim=700): def show_image_cv2(image, maxdim=700):

View File

@ -2,7 +2,7 @@ import json
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
from operator import itemgetter from operator import itemgetter
from typing import SupportsIndex, Tuple from typing import Literal, SupportsIndex, Tuple
import fitz # type: ignore import fitz # type: ignore
import numpy as np import numpy as np
@ -39,7 +39,7 @@ def transform_image_coordinates_to_pdf_coordinates(
return rect.x0, rect.y0, rect.x1, rect.y1 return rect.x0, rect.y0, rect.x1, rect.y1
def rescale_to_pdf(bbox: BBoxType, page_info: PageInfo) -> tuple[float, float, float, float]: def rescale_to_pdf(bbox: BBoxType, page_info: PageInfo) -> BBoxType:
round3 = lambda x: tuple(map(lambda y: round(y, 3), x)) round3 = lambda x: tuple(map(lambda y: round(y, 3), x))
pdf_h, pdf_w = page_info.height, page_info.width pdf_h, pdf_w = page_info.height, page_info.width
@ -54,6 +54,41 @@ def rescale_to_pdf(bbox: BBoxType, page_info: PageInfo) -> tuple[float, float, f
return round3((bbox[0] * ratio_w, bbox[1] * ratio_h, bbox[2] * ratio_w, bbox[3] * ratio_h)) return round3((bbox[0] * ratio_w, bbox[1] * ratio_h, bbox[2] * ratio_w, bbox[3] * ratio_h))
def mirror_horizontal(bbox: BBoxType, page_height: int | float) -> BBoxType:
x0, y0, x1, y1 = bbox
y0_new = page_height - y0
y1_new = page_height - y1
return x0, y0_new, x1, y1_new
def mirror_vertical(bbox: BBoxType, page_width: int | float) -> BBoxType:
x0, y0, x1, y1 = bbox
x0_new = page_width - x1
x1_new = page_width - x0
return x0_new, y0, x1_new, y1
def derotate_image(bbox: BBoxType, page_info: PageInfo) -> BBoxType:
logger.debug(f"{page_info.rotation=}")
match page_info.rotation:
case 0:
bbox = mirror_horizontal(bbox, page_info.height)
case 90:
pass
case 180:
bbox = mirror_vertical(bbox, page_info.height)
case 270:
bbox = mirror_vertical(mirror_horizontal(bbox, page_info.height), page_info.height)
case _:
logger.warning(f"Unknown rotation: {page_info.rotation}")
pass
return bbox
def transform_table_lines_by_page_info(bboxes: dict, offsets: tuple, page_info: PageInfo) -> dict: def transform_table_lines_by_page_info(bboxes: dict, offsets: tuple, page_info: PageInfo) -> dict:
transform = partial(rescale_to_pdf, page_info=page_info) transform = partial(rescale_to_pdf, page_info=page_info)
@ -67,9 +102,11 @@ def transform_table_lines_by_page_info(bboxes: dict, offsets: tuple, page_info:
return (x1 + offset_x, y1 + offset_y, x2 + offset_x, y2 + offset_y) return (x1 + offset_x, y1 + offset_y, x2 + offset_x, y2 + offset_y)
derotate = partial(derotate_image, page_info=page_info)
unpack = itemgetter("x1", "y1", "x2", "y2") unpack = itemgetter("x1", "y1", "x2", "y2")
pack = lambda x: {"x1": x[0], "y1": x[1], "x2": x[2], "y2": x[3]} pack = lambda x: {"x1": x[0], "y1": x[1], "x2": x[2], "y2": x[3]}
convert = compose(pack, apply_offsets, transform, unpack) convert = compose(pack, derotate, apply_offsets, transform, unpack)
table_lines = bboxes.get("tableLines", []) table_lines = bboxes.get("tableLines", [])
bboxes["tableLines"] = list(map(convert, table_lines)) bboxes["tableLines"] = list(map(convert, table_lines))