Convert to image size based

This commit is contained in:
James R. Barlow
2023-04-13 23:49:12 -07:00
parent 01dc8e23ff
commit 4f604591b4
3 changed files with 35 additions and 38 deletions
@@ -172,10 +172,10 @@ def filter_ocr_image(page: PageContext, image: Image.Image) -> Image.Image:
"""
options = page.options
if options.tesseract_downsample_large_images:
factor = calculate_downsample(
size = calculate_downsample(
image, max_size=(32767, 32767), max_bytes=(2**31) - 1
)
image = downsample_image(image, factor)
image = downsample_image(image, size)
return image
+26 -31
View File
@@ -4,7 +4,7 @@
"""OCR-related image manipulation."""
import logging
from math import ceil, sqrt
from math import ceil, floor, sqrt
from PIL import Image
@@ -31,12 +31,12 @@ def calculate_downsample(
max_size: tuple[int, int] | None = None,
max_pixels: int | None = None,
max_bytes: int | None = None,
) -> float:
) -> tuple[int, int]:
"""
Calculate the scaling factor required to downsample an image to fit within
Calculate the new image size required to downsample an image to fit within
the given limits.
If no limit is exceeded, 1.0 is returned.
If no limit is exceeded, the input image's size is returned.
Args:
image: The image to downsample.
@@ -46,41 +46,40 @@ def calculate_downsample(
max_bytes: The maximum number of bytes in the image. RGB is counted as 4
bytes; all other modes are counted as 1 byte.
"""
scaling_factor = 1.0
size = image.size
if max_size is not None:
major_axis = max(image.size)
if major_axis > max(max_size):
size_factor = max(max_size) / major_axis
if size_factor < 1.0:
log.debug("Resizing image to fit Tesseract image size limit")
scaling_factor = max(max_size) / major_axis
size = floor(size[0] * size_factor), floor(size[1] * size_factor)
if max_pixels is not None:
if image.size[0] * image.size[1] * scaling_factor * scaling_factor > max_pixels:
if size[0] * size[1] > max_pixels:
log.debug("Resizing image to fit image pixel limit")
scaling_factor *= sqrt(
max_pixels / (image.size[0] * image.size[1] * scaling_factor)
)
pixels_factor = sqrt(max_pixels / (image.size[0] * image.size[1]))
size = floor(size[0] * pixels_factor), floor(size[1] * pixels_factor)
if max_bytes is not None:
bpp = bytes_per_pixel(image.mode)
# stride = bytes per line
stride = ceil(image.size[0] * scaling_factor) * bpp
height = ceil(image.size[1] * scaling_factor)
size = stride * height
if size > max_bytes:
stride = size[0] * bpp
height = size[1]
if stride * height > max_bytes:
log.debug("Resizing image to fit image byte size limit")
scaling_factor *= sqrt((max_bytes - 1) / size)
scaled_bytes_per_line = ceil(image.size[0] * scaling_factor) * bpp
height = ceil(image.size[1] * scaling_factor)
size = scaled_bytes_per_line * height
assert size <= max_bytes, f"{size} > {max_bytes}"
bytes_factor = sqrt((max_bytes) / (stride * height))
scaled_stride = floor(stride * bytes_factor)
scaled_height = floor(height * bytes_factor)
size = ceil(scaled_stride / bpp), scaled_height
assert (size[0] * bpp * size[1]) <= max_bytes
return scaling_factor
return size
def downsample_image(
image: Image.Image,
scaling_factor: float,
new_size: tuple[int, int],
*,
resample_mode: Image.Resampling = Image.Resampling.BICUBIC,
reducing_gap: int = 3,
@@ -99,23 +98,19 @@ def downsample_image(
reducing_gap: The reducing gap to use when downsampling (for larger
reductions).
"""
if scaling_factor == 1.0:
if new_size == image.size:
return image
if scaling_factor > 1.0 or scaling_factor <= 0:
raise ValueError("scaling_factor must be <= 1.0 and > 0")
original_size = image.size
original_dpi = image.info['dpi']
image = image.resize(
(
ceil(image.size[0] * scaling_factor),
ceil(image.size[1] * scaling_factor),
),
new_size,
resample=resample_mode,
reducing_gap=reducing_gap,
)
image.info['dpi'] = (
original_dpi[0] * scaling_factor,
original_dpi[1] * scaling_factor,
round(original_dpi[0] * new_size[0] / original_size[0]),
round(original_dpi[1] * new_size[1] / original_size[1]),
)
log.debug(f"Rescaled image to {image.size} pixels and {image.info['dpi']} dpi")
return image