Exporting fixed
This commit is contained in:
@ -1,5 +1,42 @@
|
||||
# import dearpygui.dearpygui as dpg
|
||||
# import numpy as np
|
||||
|
||||
# from .pipeline_stage_widget import PipelineStageWidget
|
||||
|
||||
|
||||
# class ExportStage(PipelineStageWidget):
|
||||
# name = "Export Image"
|
||||
# register = True
|
||||
# has_pipeline_in = True
|
||||
# has_pipeline_out = False
|
||||
|
||||
# def __init__(self, manager, logger):
|
||||
# super().__init__(manager, logger, default_stage_out="opened_image")
|
||||
# self.manager.bus.subscribe(
|
||||
# "process_full_res", self._on_process_full_res, True)
|
||||
|
||||
# def create_pipeline_stage_content(self):
|
||||
# dpg.add_text("Some export fields")
|
||||
|
||||
# def _on_process_full_res(self, data):
|
||||
# self.logger.info("Starting full res pipeline export")
|
||||
|
||||
# def on_pipeline_data(self, img):
|
||||
# if img is None:
|
||||
# return
|
||||
# self.logger.info("low res image received, ignore")
|
||||
|
||||
# def on_full_res_pipeline_data(self, img):
|
||||
# if img is None:
|
||||
# return
|
||||
# h, w, _ = img.shape
|
||||
# self.logger.info(f"Full res image received: {w}x{h}")
|
||||
|
||||
|
||||
import os
|
||||
import dearpygui.dearpygui as dpg
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
from .pipeline_stage_widget import PipelineStageWidget
|
||||
|
||||
@ -11,23 +48,86 @@ class ExportStage(PipelineStageWidget):
|
||||
has_pipeline_out = False
|
||||
|
||||
def __init__(self, manager, logger):
|
||||
super().__init__(manager, logger, default_stage_out="opened_image")
|
||||
self.manager.bus.subscribe(
|
||||
"process_full_res", self._on_process_full_res, True)
|
||||
# we don’t register an output stage — this widget only consumes
|
||||
super().__init__(manager, logger, default_stage_out="unused")
|
||||
# tags for our “Save As” dialog
|
||||
self._save_dialog_tag = dpg.generate_uuid()
|
||||
self._save_path = None
|
||||
|
||||
def create_pipeline_stage_content(self):
|
||||
dpg.add_text("Some export fields")
|
||||
# Button to pop up the file-save dialog
|
||||
dpg.add_button(label="Save As…",
|
||||
callback=lambda s,a,u: dpg.configure_item(self._save_dialog_tag, show=True))
|
||||
|
||||
def _on_process_full_res(self, data):
|
||||
self.logger.info("Starting full res pipeline export")
|
||||
# File dialog for choosing export path & extension
|
||||
with dpg.file_dialog(
|
||||
directory_selector=False,
|
||||
show=False,
|
||||
callback=self._on_save_selected,
|
||||
tag=self._save_dialog_tag,
|
||||
width=400,
|
||||
height=300
|
||||
):
|
||||
dpg.add_file_extension("PNG {.png}")
|
||||
dpg.add_file_extension("JPEG {.jpg,.jpeg}")
|
||||
dpg.add_file_extension("TIFF {.tif,.tiff}")
|
||||
dpg.add_file_extension("All files {.*}")
|
||||
|
||||
def on_pipeline_data(self, img):
|
||||
def _on_save_selected(self, sender, app_data):
|
||||
"""
|
||||
Called when the user picks a filename in the Save As… dialog.
|
||||
Stores the path for the next full-res pass.
|
||||
"""
|
||||
# app_data is a dict with 'current_path' and 'selections'
|
||||
path = os.path.join(
|
||||
app_data["current_path"],
|
||||
app_data["file_name"]
|
||||
)
|
||||
self._save_path = path
|
||||
self.logger.info(f"Export path set to: {path}")
|
||||
|
||||
def on_pipeline_data(self, img: np.ndarray):
|
||||
# ignore all previews
|
||||
return
|
||||
|
||||
def on_full_res_pipeline_data(self, img: np.ndarray):
|
||||
"""
|
||||
Receives the full-resolution NumPy image when the user fires
|
||||
the “Run full-res pipeline” action. Saves via Pillow.
|
||||
"""
|
||||
if img is None:
|
||||
self.logger.error("on_full_res_pipeline_data called with None image")
|
||||
return
|
||||
if not self._save_path:
|
||||
self.logger.warning("No export path set — click Save As… first")
|
||||
return
|
||||
self.logger.info("low res image received, ignore")
|
||||
|
||||
def on_full_res_pipeline_data(self, img):
|
||||
if img is None:
|
||||
return
|
||||
h, w, _ = img.shape
|
||||
self.logger.info(f"Full res image received: {w}x{h}")
|
||||
# Decide bit depth by extension
|
||||
ext = os.path.splitext(self._save_path)[1].lower()
|
||||
# Convert floats → uint; or leave ints alone
|
||||
if np.issubdtype(img.dtype, np.floating):
|
||||
if ext in (".tif", ".tiff"):
|
||||
arr = np.clip(img * 65535.0, 0, 65535).astype(np.uint16)
|
||||
else:
|
||||
arr = np.clip(img * 255.0, 0, 255).astype(np.uint8)
|
||||
else:
|
||||
arr = img
|
||||
|
||||
# Determine PIL mode
|
||||
mode = None
|
||||
if arr.ndim == 2:
|
||||
mode = "L"
|
||||
elif arr.ndim == 3:
|
||||
c = arr.shape[2]
|
||||
if c == 3:
|
||||
mode = "RGB"
|
||||
elif c == 4:
|
||||
mode = "RGBA"
|
||||
|
||||
try:
|
||||
im = Image.fromarray(arr, mode) if mode else Image.fromarray(arr)
|
||||
im.save(self._save_path)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to save image to {self._save_path}: {e}")
|
||||
else:
|
||||
self.logger.info(f"Saved full-resolution image to {self._save_path}")
|
||||
|
@ -41,6 +41,9 @@ class HistogramWidget(PipelineStageWidget):
|
||||
self.img = img
|
||||
self.needs_redraw = True
|
||||
|
||||
def on_full_res_pipeline_data(self, img):
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
# TODO move calculations to on_pipeline_data
|
||||
if not self.needs_redraw or self.img is None:
|
||||
|
Reference in New Issue
Block a user