layout/extract-images/processor: write AlternativeImage if binarized…

- layout/extract-images: when running with `input_binary`, then
  pass the binarized image of the full page to the writer
- writer: if there is a binarized image, then add an `AlternativeImage`
  to the top-level of the output PAGE (with the array instead of a
  file name)
- writer.write_xml (standalone CLIs): save the image in the same
  directory as the output file, but with `.bin.png` isntead of
  `.xml` as suffix
- processor: convert the binarized image array to PIL and pass over
  as OcrdPageResultImage for the final path name
This commit is contained in:
Robert Sachunsky 2026-07-19 21:40:57 +02:00
parent f1505f5ae6
commit 63fbfc01b5
4 changed files with 25 additions and 1 deletions

View file

@ -252,6 +252,7 @@ class EynollahImageExtractor(Eynollah):
images = [image for image in images if image.area] images = [image for image in images if image.area]
pcgts = writer.build_pagexml( pcgts = writer.build_pagexml(
page=page, page=page,
img_bin=self.imread(image, binary=True) if self.input_binary else None,
num_col=num_col_classifier, num_col=num_col_classifier,
images=images, images=images,
) )

View file

@ -2098,6 +2098,7 @@ class Eynollah:
pcgts = writer.build_pagexml( pcgts = writer.build_pagexml(
page=page, page=page,
img_bin=self.imread(image, binary=True) if self.input_binary else None,
num_col=num_col_classifier, num_col=num_col_classifier,
order_of_texts=[0], order_of_texts=[0],
textregions=textregions, textregions=textregions,
@ -2170,6 +2171,7 @@ class Eynollah:
pcgts = writer.build_pagexml( pcgts = writer.build_pagexml(
page=page, page=page,
img_bin=self.imread(image, binary=True) if self.input_binary else None,
num_col=0, num_col=0,
) )
if writer.pcgts is None: if writer.pcgts is None:
@ -2370,6 +2372,7 @@ class Eynollah:
self.logger.info("Step 5/5: Output Generation") self.logger.info("Step 5/5: Output Generation")
pcgts = writer.build_pagexml( pcgts = writer.build_pagexml(
page=page, page=page,
img_bin=self.imread(image, binary=True) if self.input_binary else None,
num_col=num_col_classifier, num_col=num_col_classifier,
order_of_texts=order_text, order_of_texts=order_text,
textregions=textregions, textregions=textregions,

View file

@ -1,5 +1,6 @@
from functools import cached_property from functools import cached_property
from typing import Optional from typing import Optional
from PIL import Image
from ocrd_models import OcrdPage from ocrd_models import OcrdPage
from ocrd import OcrdPageResultImage, Processor, OcrdPageResult from ocrd import OcrdPageResultImage, Processor, OcrdPageResult
@ -95,4 +96,9 @@ class EynollahProcessor(Processor):
img_pil=page_image, pcgts=pcgts, img_pil=page_image, pcgts=pcgts,
# ocrd.Processor will handle OCRD_EXISTING_OUTPUT more flexibly # ocrd.Processor will handle OCRD_EXISTING_OUTPUT more flexibly
overwrite=True) overwrite=True)
if self.parameter['binarize'] and (img_alt := next(
(img for img in pcgts.Page.AlternativeImage
if img.comments == "binarized"), None)):
result.images.append(OcrdPageResultImage(
Image.fromarray(img_alt.filename), '.IMG-BIN', img_alt))
return result return result

View file

@ -4,12 +4,14 @@ from pathlib import Path
import os.path import os.path
import logging import logging
from typing import Optional, List, Tuple from typing import Optional, List, Tuple
import numpy as np import numpy as np
import cv2 import cv2
from shapely import affinity, clip_by_rect from shapely import affinity, clip_by_rect
from ocrd_utils import points_from_polygon from ocrd_utils import points_from_polygon
from ocrd_models.ocrd_page import ( from ocrd_models.ocrd_page import (
AlternativeImageType,
BorderType, BorderType,
CoordsType, CoordsType,
TextLineType, TextLineType,
@ -80,6 +82,13 @@ class EynollahXmlWriter:
def write_pagexml(self, pcgts): def write_pagexml(self, pcgts):
self.logger.info("output filename: '%s'", self.output_filename) self.logger.info("output filename: '%s'", self.output_filename)
if img_alt := next(
(img for img in pcgts.Page.AlternativeImage
if img.comments == "binarized"
and isinstance(img.filename, np.ndarray)), None):
img_alt_filename = self.output_filename[:-4] + '.bin.png'
cv2.imwrite(img_alt_filename, img_alt.filename)
img_alt.filename = os.path.basename(img_alt_filename)
with open(self.output_filename, 'w') as f: with open(self.output_filename, 'w') as f:
f.write(to_xml(pcgts)) f.write(to_xml(pcgts))
@ -87,7 +96,8 @@ class EynollahXmlWriter:
self, self,
*, *,
page: Region, page: Region,
num_col=1, img_bin: Optional[np.ndarray] = None,
num_col: int = 1,
order_of_texts: List[int] = [], order_of_texts: List[int] = [],
textregions: List[TextRegion] = [], textregions: List[TextRegion] = [],
textregions_h: List[TextRegion] = [], textregions_h: List[TextRegion] = [],
@ -104,6 +114,10 @@ class EynollahXmlWriter:
pcgts = self.pcgts if self.pcgts else create_page_xml( pcgts = self.pcgts if self.pcgts else create_page_xml(
self.image_filename, self.image_height, self.image_width) self.image_filename, self.image_height, self.image_width)
pcgts.Metadata.Comments = "num_col %d" % num_col pcgts.Metadata.Comments = "num_col %d" % num_col
if img_bin is not None:
img_alt = AlternativeImageType(filename=img_bin, # will be replaced later
comments="binarized")
pcgts.Page.add_AlternativeImage(img_alt)
pcgts.Page.set_custom('layout {num_col:%d;} ' % num_col) pcgts.Page.set_custom('layout {num_col:%d;} ' % num_col)
pcgts.Page.set_orientation(-page.skew) pcgts.Page.set_orientation(-page.skew)
pcgts.Page.set_Border(BorderType(Coords=CoordsType( pcgts.Page.set_Border(BorderType(Coords=CoordsType(