Exporting fixed
This commit is contained in:
@ -13,7 +13,7 @@ from .layout_manager import LayoutManager
|
||||
|
||||
from .widgets.base_widget import BaseWidget
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -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,95 @@ 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):
|
||||
with dpg.child_window(autosize_x=True, autosize_y=True, horizontal_scrollbar=True):
|
||||
self.path_label = dpg.add_text("...")
|
||||
|
||||
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}")
|
||||
dpg.set_value(self.path_label, 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)
|
||||
|
||||
# JPEG doesn’t support alpha — drop it
|
||||
if ext in (".jpg", ".jpeg") and im.mode == "RGBA":
|
||||
im = im.convert("RGB")
|
||||
|
||||
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:
|
||||
|
@ -88,7 +88,7 @@ DockId=0x00000011,0
|
||||
|
||||
[Window][###35]
|
||||
Pos=272,19
|
||||
Size=710,588
|
||||
Size=700,588
|
||||
Collapsed=0
|
||||
DockId=0x00000033,0
|
||||
|
||||
@ -100,7 +100,7 @@ DockId=0x00000020,0
|
||||
|
||||
[Window][###81]
|
||||
Pos=272,609
|
||||
Size=710,191
|
||||
Size=700,191
|
||||
Collapsed=0
|
||||
DockId=0x00000010,0
|
||||
|
||||
@ -117,8 +117,8 @@ Collapsed=0
|
||||
DockId=0x00000036,1
|
||||
|
||||
[Window][###111]
|
||||
Pos=974,630
|
||||
Size=226,170
|
||||
Pos=974,666
|
||||
Size=226,134
|
||||
Collapsed=0
|
||||
DockId=0x00000015,0
|
||||
|
||||
@ -142,7 +142,7 @@ DockId=0x00000022,0
|
||||
|
||||
[Window][###103]
|
||||
Pos=984,19
|
||||
Size=216,679
|
||||
Size=216,634
|
||||
Collapsed=0
|
||||
DockId=0x0000003F,0
|
||||
|
||||
@ -208,7 +208,7 @@ DockId=0x00000031,0
|
||||
|
||||
[Window][###95]
|
||||
Pos=974,19
|
||||
Size=226,609
|
||||
Size=226,645
|
||||
Collapsed=0
|
||||
DockId=0x00000014,0
|
||||
|
||||
@ -225,14 +225,14 @@ Collapsed=0
|
||||
DockId=0x00000039,0
|
||||
|
||||
[Window][###123]
|
||||
Pos=984,634
|
||||
Size=216,166
|
||||
Pos=984,655
|
||||
Size=216,145
|
||||
Collapsed=0
|
||||
DockId=0x0000003E,0
|
||||
|
||||
[Window][###102]
|
||||
Pos=984,19
|
||||
Size=216,781
|
||||
Size=216,613
|
||||
Collapsed=0
|
||||
DockId=0x0000003D,0
|
||||
|
||||
@ -286,8 +286,8 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Siz
|
||||
DockNode ID=0x00000021 Parent=0x0000001D SizeRef=216,579 Split=Y Selected=0x714F2F7B
|
||||
DockNode ID=0x00000019 Parent=0x00000021 SizeRef=216,391 Selected=0x714F2F7B
|
||||
DockNode ID=0x0000001A Parent=0x00000021 SizeRef=216,388 Split=Y Selected=0x7E9438EA
|
||||
DockNode ID=0x00000014 Parent=0x0000001A SizeRef=216,609 Selected=0x36EF55AB
|
||||
DockNode ID=0x00000015 Parent=0x0000001A SizeRef=216,170 Selected=0x7E9438EA
|
||||
DockNode ID=0x00000014 Parent=0x0000001A SizeRef=216,645 Selected=0x36EF55AB
|
||||
DockNode ID=0x00000015 Parent=0x0000001A SizeRef=216,134 Selected=0x7E9438EA
|
||||
DockNode ID=0x00000022 Parent=0x0000001D SizeRef=216,200 Selected=0x0D80EC84
|
||||
DockNode ID=0x0000001E Parent=0x0000001C SizeRef=216,435 Selected=0x7740BFE4
|
||||
DockNode ID=0x00000024 Parent=0x00000027 SizeRef=216,781 Split=Y Selected=0xCF08B82F
|
||||
|
Reference in New Issue
Block a user