Skip to content

Complete OCR question image persistence - #94

Merged
Helio-206 merged 2 commits into
prodfrom
ocr-image-storage
Aug 18, 2026
Merged

Complete OCR question image persistence#94
Helio-206 merged 2 commits into
prodfrom
ocr-image-storage

Conversation

@Helio-206

Copy link
Copy Markdown
Collaborator

Summary

Completes the OCR image persistence vertical slice on top of the storage foundation from the closed PR #93.

  • Keeps the ImageStorage port and local/S3-compatible adapters.
  • Retains the exact source bytes submitted to OCR for the persistence flow.
  • Extends the OCR response with versioned region metadata (contractVersion: 1), bounded bbox, source dimensions, page index, and source-file index.
  • Crops questao_N regions from validated raster sources, stores normalized PNG objects, and associates them with the persisted QuestionImage row.
  • Cleans stored objects when database persistence fails and never uses client filenames as storage paths.
  • Keeps PDF pages and cabecalho/rodape metadata-only until a statement-asset/PDF rendering contract exists.

Explicit boundary

This PR makes automatic question-image persistence safe for raster uploads. It does not claim that PDF-rendered pages can be reconstructed by the backend, nor does it add a new statement-assets table for headers and footers. Issue #69 remains open for those follow-up cases.

Validation

  • ./mvnw -B test: 35 tests passed.
  • OCR focused suite in the project image: 56 passed, 1 skipped because it requires --runslow.
  • OCR region edge suite: 7 passed.
  • python3 -m py_compile for modified OCR modules: passed.
  • git diff --check: passed.

Security and operations

  • Source upload buffering is limited to 20 MiB per file and 50 MiB aggregate for persistence.
  • Region crops are decoded as raster images and re-encoded as PNG; invalid or ambiguous source mappings are skipped.
  • Storage keys are generated server-side.
  • Production object storage must remain private and use controlled delivery; the anonymous MinIO policy is local-development-only.

Replaces the closed PR #93 after the branch was renamed from the historical agent/* name to ocr-image-storage.

@Helio-206
Helio-206 marked this pull request as ready for review August 18, 2026 14:23
@Helio-206
Helio-206 merged commit d625220 into prod Aug 18, 2026
2 checks passed
@Helio-206
Helio-206 deleted the ocr-image-storage branch August 18, 2026 14:23

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 555af3dece

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

metadata, questions, images_to_upload, unmapped, warnings = self.postprocessor.process(
text_blocks,
page_count=1,
page_dimensions={page_index: (int(image.shape[1]), int(image.shape[0]))},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Transform crop coordinates back through deskewing

When default deskewing rotates a page, OCR bounding boxes are measured on processed_image, whose dimensions and origin can differ from the original raster, but this contract labels those coordinates with the original image dimensions. The backend only applies independent X/Y scaling, so it cannot undo the rotation and translation; persisted crops from skewed scans will consequently capture the wrong page region. Either map the boxes through the inverse preprocessing transform or derive regions in the original coordinate space.

Useful? React with 👍 / 👎.

Comment on lines +1489 to +1493
bbox = region_bbox(
question.page_index,
question.start_y,
max(question.end_y, question.start_y + 1),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Extend question crops to include unrecognized figures

For a question whose prompt references a figure followed by an image containing no OCR text, end_y remains the bottom of the last recognized question block. Passing that value here means the generated box ends only 24 pixels below the prompt, so a typical figure placed beneath the prompt—and especially one followed immediately by the next question—is excluded from the persisted image. The region should extend to the next question boundary or otherwise include the non-text visual area.

Useful? React with 👍 / 👎.

Comment on lines +169 to +172
private boolean isRaster(OcrUploadedFile file) {
try {
return ImageIO.read(new ByteArrayInputStream(file.content())) != null;
} catch (IOException | RuntimeException error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add WebP decoding before filtering OCR image sources

When /extract-and-persist receives an allowed WebP source, standard JDK ImageIO has no WebP reader and the backend declares no ImageIO WebP provider, so this check returns false and silently drops every OCR image candidate. This makes the new persistence behavior fail specifically for a format advertised by OcrController; decode WebP with a supported library or remove it from the persistence endpoint's accepted formats.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant