Skip to content

Commit

Permalink
Move from OpenCV to vips
Browse files Browse the repository at this point in the history
  • Loading branch information
Malacath-92 committed Oct 18, 2024
1 parent 8997642 commit 72305dd
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 63 deletions.
1 change: 1 addition & 0 deletions build_exe.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def run_nuitka(debug, package):

subprocess.check_call(nuitka_args)


def main():
parser = argparse.ArgumentParser(
description="Build print-proxy-prep.exe, run from project root"
Expand Down
16 changes: 12 additions & 4 deletions gui_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,9 @@ def render_work():
print(e)

self.window().setEnabled(False)
render_window = popup(self.window(), "Rendering PDF...", application._debug_mode)
render_window = popup(
self.window(), "Rendering PDF...", application._debug_mode
)
render_window.show_during_work(render_work)
del render_window
self.window().setEnabled(True)
Expand Down Expand Up @@ -1279,7 +1281,9 @@ def cropper_work():
del print_dict["cards"][img]

self.window().setEnabled(False)
crop_window = popup(self.window(), "Cropping images...", application._debug_mode)
crop_window = popup(
self.window(), "Cropping images...", application._debug_mode
)
crop_window.show_during_work(cropper_work)
del crop_window
if self._rebuild_after_cropper:
Expand Down Expand Up @@ -1314,7 +1318,9 @@ def load_project():
)

self.window().setEnabled(False)
reload_window = popup(self.window(), "Reloading project...", application._debug_mode)
reload_window = popup(
self.window(), "Reloading project...", application._debug_mode
)
reload_window.show_during_work(load_project)
del reload_window
self.window().refresh_widgets(print_dict)
Expand Down Expand Up @@ -1345,7 +1351,9 @@ def reload_work():
)

self.window().setEnabled(False)
reload_window = popup(self.window(), "Reloading project...", application._debug_mode)
reload_window = popup(
self.window(), "Reloading project...", application._debug_mode
)
reload_window.show_during_work(reload_work)
del reload_window
self.window().refresh(print_dict, img_dict)
Expand Down
107 changes: 51 additions & 56 deletions image.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import io
import cv2
import json
import numpy
import base64
from enum import Enum

from PIL import Image, ImageFilter
from PIL import Image as PIL_Image
from PIL import ImageFilter as PIL_ImageFilter
import pyvips

from util import *
from constants import *
Expand Down Expand Up @@ -33,7 +33,7 @@ def init():
lut_table = [row2val(row) for row in lut_raw]

global vibrance_cube
vibrance_cube = ImageFilter.Color3DLUT(lsize, lut_table)
vibrance_cube = PIL_ImageFilter.Color3DLUT(lsize, lut_table)


def init_image_folder(image_dir, crop_dir):
Expand All @@ -48,29 +48,23 @@ class Rotation(Enum):
Rotate_180 = (2,)


def rotate_image(img, rotation):
def rotate_image(img: pyvips.Image, rotation) -> pyvips.Image:
match rotation:
case Rotation.RotateClockwise_90:
rotation = cv2.ROTATE_90_CLOCKWISE
rotation = img.rot90
case Rotation.RotateCounterClockwise_90:
rotation = cv2.ROTATE_90_COUNTERCLOCKWISE
rotation = img.rot270
case Rotation.Rotate_180:
rotation = cv2.ROTATE_180
return cv2.rotate(img, rotation)
rotation = img.rot180
return rotation()


def read_image(path):
with open(path, "rb") as f:
bytes = bytearray(f.read())
numpyarray = numpy.asarray(bytes, dtype=numpy.uint8)
image = cv2.imdecode(numpyarray, cv2.IMREAD_UNCHANGED)
return image
def read_image(path) -> pyvips.Image:
return pyvips.Image.new_from_file(path)


def write_image(path, image):
with open(path, "wb") as f:
_, bytes = cv2.imencode(".png", image)
bytes.tofile(f)
def write_image(path, image: pyvips.Image):
image.write_to_file(path)


def need_run_cropper(image_dir, crop_dir, bleed_edge, do_vibrance_bump):
Expand All @@ -90,10 +84,12 @@ def need_run_cropper(image_dir, crop_dir, bleed_edge, do_vibrance_bump):
return sorted(input_files) != sorted(output_files)


def crop_image(image, image_name, bleed_edge, max_dpi, print_fn=None):
def crop_image(
image: pyvips.Image, image_name, bleed_edge, max_dpi, print_fn=None
) -> pyvips.Image:
print_fn = print_fn if print_fn is not None else lambda *args: args

(h, w, _) = image.shape
w, h = image.width, image.height
(bw, bh) = card_size_with_bleed_inch
dpi = min(w / bw, h / bh)
c = round(0.12 * dpi)
Expand All @@ -108,37 +104,34 @@ def crop_image(image, image_name, bleed_edge, max_dpi, print_fn=None):
print_fn(
f"Cropping images...\n{image_name} - DPI calculated: {dpi}, cropping {c} pixels around frame"
)
cropped_image = image[c : h - c, c : w - c]
(h, w, _) = cropped_image.shape
cropped_image: pyvips.Image = image.crop(c, c, w - c * 2, h - c * 2)
w, h = image.width, image.height
if max_dpi is not None and dpi > max_dpi:
new_size = (
int(round(w * max_dpi / dpi)),
int(round(h * max_dpi / dpi)),
)
new_w = int(round(w * max_dpi / dpi))
new_h = int(round(h * max_dpi / dpi))

print_fn(
f"Cropping images...\n{image_name} - Exceeds maximum DPI {max_dpi}, resizing to {new_size[0]}x{new_size[1]}"
)
cropped_image = cv2.resize(
cropped_image, new_size, interpolation=cv2.INTER_CUBIC
)
cropped_image = numpy.array(
Image.fromarray(cropped_image).filter(ImageFilter.UnsharpMask(1, 20, 8))
f"Cropping images...\n{image_name} - Exceeds maximum DPI {max_dpi}, resizing to {new_w}x{new_h}"
)

scale = max_dpi / dpi
cropped_image = cropped_image.resize(scale, kernel=pyvips.enums.Kernel.CUBIC)
return cropped_image


def uncrop_image(image, image_name, print_fn=None):
def uncrop_image(image: pyvips.Image, image_name, print_fn=None) -> pyvips.Image:
print_fn = print_fn if print_fn is not None else lambda *args: args

(h, w, _) = image.shape
w, h = image.width, image.height
(bw, bh) = card_size_without_bleed_inch
dpi = min(w / bw, h / bh)
c = round(dpi * 0.12)
print_fn(
f"Reinserting bleed edge...\n{image_name} - DPI calculated: {dpi}, adding {c} pixels around frame"
)

return cv2.copyMakeBorder(image, c, c, c, c, cv2.BORDER_CONSTANT, value=0xFFFFFFFF)
uncropped_image = pyvips.Image.black(w + c * 2, h + c * 2)
return uncropped_image.insert(image, c, c)


def cropper(
Expand Down Expand Up @@ -194,8 +187,8 @@ def cropper(
image = read_image(os.path.join(image_dir, img_file))
cropped_image = crop_image(image, img_file, bleed_edge, max_dpi, print_fn)
if do_vibrance_bump:
cropped_image = numpy.array(
Image.fromarray(cropped_image).filter(vibrance_cube)
cropped_image = pyvips.Image.new_from_array(
PIL_Image.fromarray(cropped_image.numpy()).filter(vibrance_cube)
)
write_image(os.path.join(output_dir, img_file), cropped_image)

Expand All @@ -219,44 +212,46 @@ def cropper(
cache_previews(img_cache, image_dir, crop_dir, print_fn, img_dict)


def image_from_bytes(bytes):
def image_from_bytes(bytes) -> pyvips.Image:
img: pyvips.Image = None
try:
dataBytesIO = io.BytesIO(base64.b64decode(bytes))
buffer = dataBytesIO.getbuffer()
img = cv2.imdecode(numpy.frombuffer(buffer, numpy.uint8), -1)
buffer = dataBytesIO.getvalue()
img = pyvips.Image.new_from_buffer(buffer, options="")
except Exception as e:
pass

if img is None:
dataBytesIO = io.BytesIO(bytes)
buffer = dataBytesIO.getbuffer()
img = cv2.imdecode(numpy.frombuffer(buffer, numpy.uint8), -1)
buffer = dataBytesIO.getvalue()
img = pyvips.Image.new_from_buffer(buffer, options="")

return img


def image_to_bytes(img):
_, buffer = cv2.imencode(".png", img)
def image_to_bytes(img: pyvips.Image):
buffer = img.write_to_buffer(".png")
bio = io.BytesIO(buffer)
return bio.getvalue()


def to_bytes(file_or_bytes, resize=None):
if isinstance(file_or_bytes, numpy.ndarray):
if isinstance(file_or_bytes, pyvips.Image):
img = file_or_bytes
elif isinstance(file_or_bytes, str):
img = read_image(file_or_bytes)
else:
img = image_from_bytes(file_or_bytes)

(cur_height, cur_width, _) = img.shape
cur_width, cur_height = img.width, img.height
if resize:
new_width, new_height = resize
scale = min(new_height / cur_height, new_width / cur_width)
img = cv2.resize(
img,
(int(cur_width * scale), int(cur_height * scale)),
interpolation=cv2.INTER_AREA,
scale = new_width / cur_width
img = img.resize(
scale,
kernel=pyvips.enums.Kernel.NEAREST,
)
cur_height, cur_width = new_height, new_width
cur_width, cur_height = new_width, new_height
return image_to_bytes(img), (cur_width, cur_height)


Expand Down Expand Up @@ -299,7 +294,7 @@ def cache_previews(file, image_dir, crop_dir, print_fn, data):

if need_img:
img = read_image(os.path.join(crop_dir, f))
(h, w, _) = img.shape
w, h = img.width, img.height
scale = 248 / w
preview_size = (round(w * scale), round(h * scale))

Expand Down Expand Up @@ -330,7 +325,7 @@ def cache_previews(file, image_dir, crop_dir, print_fn, data):
has_img = "uncropped" in img_dict
if not has_img:
img = read_image(os.path.join(image_dir, f))
(h, w, _) = img.shape
w, h = img.width, img.height
scale = 186 / w
uncropped_size = (round(w * scale), round(h * scale))

Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PyQt6
reportlab
opencv-python
numpy==1.22.0
pyinstaller==5.*
pyvips
pyvips-binary
numpy==1.22.0

0 comments on commit 72305dd

Please sign in to comment.