RAW loading and processing widget added
This commit is contained in:
@ -25,6 +25,9 @@ class OpenImageWidget(PipelineStageWidget):
|
||||
height=300,
|
||||
width=400,
|
||||
):
|
||||
dpg.add_file_extension(
|
||||
"Image files {.png,.jpg,.jpeg,.bmp .gif,.tif,.tiff}",
|
||||
)
|
||||
dpg.add_file_extension(".*")
|
||||
dpg.add_button(label="Open File...", callback=self._on_open_file)
|
||||
|
||||
@ -33,7 +36,8 @@ class OpenImageWidget(PipelineStageWidget):
|
||||
|
||||
def _on_file_selected(self, sender, app_data):
|
||||
selection = (
|
||||
f"{app_data['current_path']}/{list(app_data['selections'].keys())[0]}"
|
||||
f"{app_data['current_path']
|
||||
}/{list(app_data['selections'].keys())[0]}"
|
||||
if isinstance(app_data, dict)
|
||||
else None
|
||||
)
|
||||
@ -42,7 +46,8 @@ class OpenImageWidget(PipelineStageWidget):
|
||||
self.logger.info(f"Selected file '{selection}'")
|
||||
try:
|
||||
img = Image.open(selection).convert("RGBA")
|
||||
arr = np.asarray(img).astype(np.float32) / 255.0 # normalize to [0,1]
|
||||
arr = np.asarray(img).astype(np.float32) / \
|
||||
255.0 # normalize to [0,1]
|
||||
# Publish into pipeline
|
||||
self.manager.pipeline.publish(self.pipeline_stage_out_id, arr)
|
||||
except Exception as e:
|
||||
|
205
negstation/widgets/open_raw_widget.py
Normal file
205
negstation/widgets/open_raw_widget.py
Normal file
@ -0,0 +1,205 @@
|
||||
import dearpygui.dearpygui as dpg
|
||||
import rawpy
|
||||
import numpy as np
|
||||
|
||||
from .pipeline_stage_widget import PipelineStageWidget
|
||||
|
||||
|
||||
class OpenRawWidget(PipelineStageWidget):
|
||||
name = "Open RAW File"
|
||||
register = True
|
||||
has_pipeline_in = False
|
||||
has_pipeline_out = True
|
||||
|
||||
def __init__(self, manager, logger):
|
||||
super().__init__(manager, logger, default_stage_out="opened_raw")
|
||||
self.dialog_tag = dpg.generate_uuid()
|
||||
self.output_tag = dpg.generate_uuid()
|
||||
self.config_group = dpg.generate_uuid()
|
||||
self.busy_group = dpg.generate_uuid()
|
||||
self.raw_path = None
|
||||
self.config = {
|
||||
# Demosaic algorithm
|
||||
"demosaic_algorithm": rawpy.DemosaicAlgorithm.AHD,
|
||||
# Output color space
|
||||
"output_color": rawpy.ColorSpace.sRGB,
|
||||
# Bits per sample
|
||||
"output_bps": 16,
|
||||
# White balance
|
||||
"use_camera_wb": True,
|
||||
"use_auto_wb": False,
|
||||
"user_wb": (1.0, 1.0, 1.0, 1.0),
|
||||
# Brightness/exposure
|
||||
"bright": 1.0,
|
||||
"no_auto_bright": False,
|
||||
# Gamma correction (you’ll pass (1.0, config["gamma"]) down)
|
||||
"gamma": 1.0,
|
||||
# Size & quality toggles
|
||||
"half_size": False,
|
||||
"four_color_rgb": False,
|
||||
}
|
||||
|
||||
def create_pipeline_stage_content(self):
|
||||
with dpg.file_dialog(
|
||||
directory_selector=False,
|
||||
show=False,
|
||||
callback=self._on_file_selected,
|
||||
tag=self.dialog_tag,
|
||||
height=300,
|
||||
width=400,
|
||||
):
|
||||
dpg.add_file_extension(
|
||||
"RAW files {.nef,.cr2}",
|
||||
)
|
||||
dpg.add_file_extension(".*")
|
||||
|
||||
with dpg.group(tag=self.config_group):
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_button(label="Open File...",
|
||||
callback=self._on_open_file)
|
||||
dpg.add_button(label="Reprocess",
|
||||
callback=self._process_and_publish)
|
||||
|
||||
dpg.add_combo(
|
||||
label="Demosaic",
|
||||
items=[alg.name for alg in rawpy.DemosaicAlgorithm],
|
||||
default_value=rawpy.DemosaicAlgorithm.AHD.name,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"demosaic_algorithm", rawpy.DemosaicAlgorithm[a])
|
||||
)
|
||||
dpg.add_combo(
|
||||
label="Color Space",
|
||||
items=[cs.name for cs in rawpy.ColorSpace],
|
||||
default_value=rawpy.ColorSpace.sRGB.name,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"output_color", rawpy.ColorSpace[a])
|
||||
)
|
||||
dpg.add_combo(
|
||||
label="Output Bits",
|
||||
items=["8", "16"],
|
||||
default_value="16",
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"output_bps", int(a))
|
||||
)
|
||||
dpg.add_checkbox(
|
||||
label="Use Camera WB",
|
||||
default_value=True,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"use_camera_wb", a)
|
||||
)
|
||||
dpg.add_checkbox(
|
||||
label="Auto WB",
|
||||
default_value=False,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"use_auto_wb", a)
|
||||
)
|
||||
dpg.add_slider_float(
|
||||
label="Manual WB R Gain",
|
||||
default_value=1.0, min_value=0.1, max_value=4.0,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"user_wb", (a, self.config["user_wb"][1], self.config["user_wb"][2], self.config["user_wb"][3]))
|
||||
)
|
||||
dpg.add_slider_float(
|
||||
label="Manual WB G Gain",
|
||||
default_value=1.0, min_value=0.1, max_value=4.0,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"user_wb", (self.config["user_wb"][0], a, a, self.config["user_wb"][3]))
|
||||
)
|
||||
dpg.add_slider_float(
|
||||
label="Manual WB B Gain",
|
||||
default_value=1.0, min_value=0.1, max_value=4.0,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"user_wb", (self.config["user_wb"][0], self.config["user_wb"][1], self.config["user_wb"][2], a))
|
||||
)
|
||||
dpg.add_slider_float(
|
||||
label="Bright",
|
||||
default_value=1.0, min_value=0.1, max_value=4.0,
|
||||
callback=lambda s, a, u: self.config.__setitem__("bright", a)
|
||||
)
|
||||
dpg.add_checkbox(
|
||||
label="No Auto Bright",
|
||||
default_value=False,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"no_auto_bright", a)
|
||||
)
|
||||
dpg.add_slider_float(
|
||||
label="Gamma",
|
||||
default_value=1.0, min_value=0.1, max_value=3.0,
|
||||
callback=lambda s, a, u: self.config.__setitem__("gamma", a)
|
||||
)
|
||||
dpg.add_checkbox(
|
||||
label="Half-size",
|
||||
default_value=False,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"half_size", a)
|
||||
)
|
||||
dpg.add_checkbox(
|
||||
label="4-color RGB",
|
||||
default_value=False,
|
||||
callback=lambda s, a, u: self.config.__setitem__(
|
||||
"four_color_rgb", a)
|
||||
)
|
||||
|
||||
with dpg.group(tag=self.busy_group):
|
||||
dpg.add_text("Processing...")
|
||||
|
||||
def _on_open_file(self):
|
||||
dpg.configure_item(self.dialog_tag, show=True)
|
||||
|
||||
def _on_file_selected(self, sender, app_data):
|
||||
selection = (
|
||||
f"{app_data['current_path']
|
||||
}/{list(app_data['selections'].keys())[0]}"
|
||||
if isinstance(app_data, dict)
|
||||
else None
|
||||
)
|
||||
if not selection:
|
||||
return
|
||||
self.raw_path = selection
|
||||
self.logger.info(f"Selected file '{selection}'")
|
||||
self._process_and_publish()
|
||||
|
||||
def _process_and_publish(self):
|
||||
if self.raw_path is None:
|
||||
return
|
||||
self.logger.info("Processing RAW image")
|
||||
|
||||
dpg.configure_item(self.config_group, show=False)
|
||||
dpg.configure_item(self.busy_group, show=True)
|
||||
|
||||
with rawpy.imread(self.raw_path) as raw:
|
||||
# Prepare postprocess kwargs from config
|
||||
postprocess_args = {
|
||||
'demosaic_algorithm': self.config["demosaic_algorithm"],
|
||||
'output_color': self.config["output_color"],
|
||||
'output_bps': self.config["output_bps"],
|
||||
'bright': self.config["bright"],
|
||||
'no_auto_bright': self.config["no_auto_bright"],
|
||||
'gamma': (1.0, self.config["gamma"]),
|
||||
'half_size': self.config["half_size"],
|
||||
'four_color_rgb': self.config["four_color_rgb"],
|
||||
}
|
||||
|
||||
if self.config["use_camera_wb"]:
|
||||
postprocess_args['use_camera_wb'] = True
|
||||
elif self.config["use_auto_wb"]:
|
||||
postprocess_args['use_auto_wb'] = True
|
||||
else:
|
||||
postprocess_args['user_wb'] = self.config["user_wb"]
|
||||
|
||||
# Postprocess into RGB
|
||||
rgb = raw.postprocess(**postprocess_args)
|
||||
|
||||
# Normalize to float32 in 0.0-1.0 range depending on output_bps
|
||||
max_val = (2 ** self.config["output_bps"]) - 1
|
||||
rgb_float = rgb.astype(np.float32) / max_val
|
||||
|
||||
# Add alpha channel (fully opaque)
|
||||
h, w, _ = rgb_float.shape
|
||||
alpha = np.ones((h, w, 1), dtype=np.float32)
|
||||
|
||||
rgba = np.concatenate([rgb_float, alpha], axis=2)
|
||||
|
||||
self.manager.pipeline.publish(self.pipeline_stage_out_id, rgba)
|
||||
dpg.configure_item(self.config_group, show=True)
|
||||
dpg.configure_item(self.busy_group, show=False)
|
@ -28,6 +28,8 @@ class PipelineStageViewer(PipelineStageWidget):
|
||||
|
||||
def on_pipeline_data(self, img):
|
||||
# Resize if needed
|
||||
if img is None:
|
||||
return
|
||||
h, w, _ = img.shape
|
||||
max_dim = 500
|
||||
scale = min(1.0, max_dim / w, max_dim / h)
|
||||
@ -66,7 +68,7 @@ class PipelineStageViewer(PipelineStageWidget):
|
||||
avail_w = win_w
|
||||
avail_h = win_h
|
||||
|
||||
scale = min(avail_w / w, avail_h / h) #, 1.0)
|
||||
scale = min(avail_w / w, avail_h / h) # , 1.0)
|
||||
disp_w = int(w * scale)
|
||||
disp_h = int(h * scale)
|
||||
|
||||
|
@ -51,9 +51,9 @@ DockId=0x00000008,0
|
||||
|
||||
[Window][###23]
|
||||
Pos=0,19
|
||||
Size=250,459
|
||||
Size=305,85
|
||||
Collapsed=0
|
||||
DockId=0x00000017,0
|
||||
DockId=0x00000021,0
|
||||
|
||||
[Window][###32]
|
||||
Pos=0,376
|
||||
@ -108,30 +108,64 @@ Pos=237,120
|
||||
Size=300,200
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=800,581 Split=X
|
||||
DockNode ID=0x00000009 Parent=0x7C6B3D9B SizeRef=250,581 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=147,355 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000D Parent=0x0000000B SizeRef=250,304 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000F Parent=0x0000000D SizeRef=250,459 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000011 Parent=0x0000000F SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000015 Parent=0x00000013 SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000017 Parent=0x00000015 SizeRef=250,379 Selected=0xD36850C8
|
||||
DockNode ID=0x00000018 Parent=0x00000015 SizeRef=250,200 Selected=0x3BEDC6B0
|
||||
DockNode ID=0x00000016 Parent=0x00000013 SizeRef=250,200 Selected=0xC7B9E77E
|
||||
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=250,200 Selected=0x3BEDC6B0
|
||||
DockNode ID=0x00000012 Parent=0x0000000F SizeRef=250,200 Selected=0x83A5C17B
|
||||
DockNode ID=0x00000010 Parent=0x0000000D SizeRef=250,120 Selected=0xAA145F7D
|
||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=250,275 Selected=0x1834836D
|
||||
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=147,224 Selected=0x2554AADD
|
||||
DockNode ID=0x0000000A Parent=0x7C6B3D9B SizeRef=548,581 Split=X
|
||||
DockNode ID=0x00000005 Parent=0x0000000A SizeRef=288,581 Split=Y Selected=0xEE087978
|
||||
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=147,427 Selected=0xEE087978
|
||||
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=147,152 Selected=0x62F4D00D
|
||||
DockNode ID=0x00000006 Parent=0x0000000A SizeRef=510,581 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=299,581 Split=Y Selected=0xA4B861D9
|
||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=299,379 Selected=0xA4B861D9
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=299,200 Selected=0xEDB425AD
|
||||
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=499,581 CentralNode=1 Selected=0x38519A65
|
||||
[Window][###43]
|
||||
Pos=307,19
|
||||
Size=493,581
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][###35]
|
||||
Pos=0,500
|
||||
Size=305,100
|
||||
Collapsed=0
|
||||
DockId=0x0000001A,0
|
||||
|
||||
[Window][###60]
|
||||
Pos=0,192
|
||||
Size=250,306
|
||||
Collapsed=0
|
||||
DockId=0x00000020,0
|
||||
|
||||
[Window][###51]
|
||||
Pos=0,106
|
||||
Size=305,392
|
||||
Collapsed=0
|
||||
DockId=0x00000022,0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,19 Size=800,581 Split=X
|
||||
DockNode ID=0x00000009 Parent=0x7C6B3D9B SizeRef=305,581 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000B Parent=0x00000009 SizeRef=147,355 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000D Parent=0x0000000B SizeRef=250,304 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000000F Parent=0x0000000D SizeRef=250,459 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000011 Parent=0x0000000F SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000015 Parent=0x00000013 SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000017 Parent=0x00000015 SizeRef=250,379 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000019 Parent=0x00000017 SizeRef=250,479 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000001B Parent=0x00000019 SizeRef=250,161 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000001D Parent=0x0000001B SizeRef=250,192 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x0000001F Parent=0x0000001D SizeRef=250,171 Split=Y Selected=0xD36850C8
|
||||
DockNode ID=0x00000021 Parent=0x0000001F SizeRef=250,85 Selected=0xD36850C8
|
||||
DockNode ID=0x00000022 Parent=0x0000001F SizeRef=250,392 Selected=0xB4AD3310
|
||||
DockNode ID=0x00000020 Parent=0x0000001D SizeRef=250,306 Selected=0x0F59680E
|
||||
DockNode ID=0x0000001E Parent=0x0000001B SizeRef=250,285 Selected=0x0F59680E
|
||||
DockNode ID=0x0000001C Parent=0x00000019 SizeRef=250,316 Selected=0x0F59680E
|
||||
DockNode ID=0x0000001A Parent=0x00000017 SizeRef=250,100 Selected=0x977476CD
|
||||
DockNode ID=0x00000018 Parent=0x00000015 SizeRef=250,200 Selected=0x3BEDC6B0
|
||||
DockNode ID=0x00000016 Parent=0x00000013 SizeRef=250,200 Selected=0xC7B9E77E
|
||||
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=250,200 Selected=0x3BEDC6B0
|
||||
DockNode ID=0x00000012 Parent=0x0000000F SizeRef=250,200 Selected=0x83A5C17B
|
||||
DockNode ID=0x00000010 Parent=0x0000000D SizeRef=250,120 Selected=0xAA145F7D
|
||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=250,275 Selected=0x1834836D
|
||||
DockNode ID=0x0000000C Parent=0x00000009 SizeRef=147,224 Selected=0x2554AADD
|
||||
DockNode ID=0x0000000A Parent=0x7C6B3D9B SizeRef=493,581 Split=X
|
||||
DockNode ID=0x00000005 Parent=0x0000000A SizeRef=288,581 Split=Y Selected=0xEE087978
|
||||
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=147,427 Selected=0xEE087978
|
||||
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=147,152 Selected=0x62F4D00D
|
||||
DockNode ID=0x00000006 Parent=0x0000000A SizeRef=510,581 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=299,581 Split=Y Selected=0xA4B861D9
|
||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=299,379 Selected=0xA4B861D9
|
||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=299,200 Selected=0xEDB425AD
|
||||
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=499,581 CentralNode=1 Selected=0x0531B3D5
|
||||
|
||||
|
@ -10,5 +10,9 @@
|
||||
{
|
||||
"widget_type": "PipelineStageViewer",
|
||||
"config": {}
|
||||
},
|
||||
{
|
||||
"widget_type": "OpenRawWidget",
|
||||
"config": {}
|
||||
}
|
||||
]
|
Reference in New Issue
Block a user