forked from AcademySoftwareFoundation/OpenColorIO
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement initial chromaticity inspector.
- Loading branch information
Showing
5 changed files
with
279 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# Copyright Contributors to the OpenColorIO Project. | ||
|
||
from .chromaticities_inspector import ChromaticitiesInspector | ||
from .code_inspector import CodeInspector | ||
from .curve_inspector import CurveInspector | ||
from .log_inspector import LogInspector |
110 changes: 110 additions & 0 deletions
110
src/apps/ocioview/ocioview/inspect/chromaticities_inspector.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# Copyright Contributors to the OpenColorIO Project. | ||
|
||
import numpy as np | ||
import pygfx as gfx | ||
import OpenImageIO as oiio | ||
import PyOpenColorIO as ocio | ||
from colour_visuals import * | ||
from PySide6 import QtCore, QtGui, QtWidgets | ||
from typing import Optional | ||
|
||
from ..viewer import WgpuCanvasOffScreenViewer | ||
from ..message_router import MessageRouter | ||
from ..utils import get_glyph_icon | ||
|
||
|
||
class ChromaticitiesInspector(QtWidgets.QWidget): | ||
@classmethod | ||
def label(cls) -> str: | ||
return "Chromaticities" | ||
|
||
@classmethod | ||
def icon(cls) -> QtGui.QIcon: | ||
return get_glyph_icon("mdi6.grain") | ||
|
||
def __init__(self, parent: Optional[QtCore.QObject] = None): | ||
super().__init__(parent=parent) | ||
|
||
self._cpu_proc = None | ||
self._image_buf = None | ||
|
||
self._wgpu_viewer = WgpuCanvasOffScreenViewer() | ||
self._setup_scene() | ||
|
||
# Layout | ||
layout = QtWidgets.QHBoxLayout() | ||
self.setLayout(layout) | ||
layout.addWidget(self._wgpu_viewer) | ||
|
||
msg_router = MessageRouter.get_instance() | ||
# msg_router.processor_ready.connect(self._on_processor_ready) | ||
# msg_router.image_ready.connect(self._on_image_ready) | ||
|
||
@property | ||
def wgpu_viewer(self): | ||
return self._wgpu_viewer | ||
|
||
def _setup_scene(self): | ||
self._wgpu_viewer.wgpu_scene.add( | ||
gfx.Background( | ||
None, gfx.BackgroundMaterial(np.array([0.18, 0.18, 0.18])) | ||
) | ||
) | ||
visuals = [ | ||
VisualGrid(size=2), | ||
VisualChromaticityDiagramCIE1931( | ||
kwargs_visual_chromaticity_diagram={"opacity": 0.25} | ||
), | ||
VisualRGBColourspace2D("ACEScg"), | ||
VisualRGBColourspace2D( | ||
"Display P3", colours=np.array([0.5, 0.5, 0.5]) | ||
), | ||
VisualRGBColourspace3D("Display P3", opacity=0.5, wireframe=True), | ||
VisualRGBScatter3D(np.random.random([24, 32, 3]), "ACEScg"), | ||
] | ||
|
||
group = gfx.Group() | ||
for visual in visuals: | ||
group.add(visual) | ||
self._wgpu_viewer.wgpu_scene.add(group) | ||
|
||
def reset(self) -> None: | ||
pass | ||
|
||
def showEvent(self, event: QtGui.QShowEvent) -> None: | ||
"""Start listening for processor updates, if visible.""" | ||
super().showEvent(event) | ||
|
||
msg_router = MessageRouter.get_instance() | ||
# msg_router.set_processor_updates_allowed(True) | ||
# msg_router.set_image_updates_allowed(True) | ||
|
||
def hideEvent(self, event: QtGui.QHideEvent) -> None: | ||
"""Stop listening for processor updates, if not visible.""" | ||
super().hideEvent(event) | ||
|
||
msg_router = MessageRouter.get_instance() | ||
# msg_router.set_processor_updates_allowed(False) | ||
# msg_router.set_image_updates_allowed(False) | ||
|
||
@QtCore.Slot(ocio.CPUProcessor) | ||
def _on_processor_ready(self, cpu_proc: ocio.CPUProcessor) -> None: | ||
self._cpu_proc = cpu_proc | ||
|
||
print("_on_processor_ready") | ||
|
||
@QtCore.Slot(np.ndarray) | ||
def _on_image_ready(self, image_buf: oiio.ImageBuf) -> None: | ||
self._image_buf = image_buf | ||
|
||
print("_on_image_ready") | ||
|
||
|
||
if __name__ == "__main__": | ||
application = QtWidgets.QApplication([]) | ||
chromaticity_inspector = ChromaticitiesInspector() | ||
chromaticity_inspector.resize(800, 600) | ||
chromaticity_inspector.show() | ||
|
||
application.exec() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# SPDX-License-Identifier: BSD-3-Clause | ||
# Copyright Contributors to the OpenColorIO Project. | ||
|
||
import numpy as np | ||
import pygfx as gfx | ||
from PySide6 import QtCore, QtGui, QtWidgets | ||
from wgpu.gui.offscreen import WgpuCanvas | ||
from wgpu.gui.qt import BUTTON_MAP, MODIFIERS_MAP | ||
|
||
|
||
class WgpuCanvasOffScreenViewer(QtWidgets.QGraphicsView): | ||
def __init__(self): | ||
super().__init__() | ||
|
||
# WebGPU | ||
self._wgpu_canvas = WgpuCanvas(size=self._viewport_size) | ||
self._wgpu_renderer = gfx.renderers.WgpuRenderer(self._wgpu_canvas) | ||
self._wgpu_camera = gfx.PerspectiveCamera(50, 16 / 9) | ||
self._wgpu_controller = gfx.OrbitController(self._wgpu_camera) | ||
self._wgpu_controller.register_events(self._wgpu_renderer) | ||
|
||
self._wgpu_scene = gfx.Scene() | ||
|
||
self._wgpu_canvas.request_draw( | ||
lambda: self._wgpu_renderer.render( | ||
self._wgpu_scene, self._wgpu_camera | ||
) | ||
) | ||
|
||
self._wgpu_camera.local.position = np.array([-0.25, -0.5, 2]) | ||
self._wgpu_camera.show_pos(np.array([1 / 3, 1 / 3, 0.4])) | ||
|
||
# QGraphicsView | ||
self.setScene(QtWidgets.QGraphicsScene(self)) | ||
self.setTransformationAnchor(self.ViewportAnchor.AnchorUnderMouse) | ||
self.image_plane = QtWidgets.QGraphicsPixmapItem( | ||
self._render_to_pixmap() | ||
) | ||
self.scene().addItem(self.image_plane) | ||
self.scale(0.5, 0.5) | ||
|
||
@property | ||
def wgpu_canvas(self): | ||
return self._wgpu_canvas | ||
|
||
@property | ||
def wgpu_renderer(self): | ||
return self._wgpu_renderer | ||
|
||
@property | ||
def wgpu_camera(self): | ||
return self._wgpu_camera | ||
|
||
@property | ||
def wgpu_controller(self): | ||
return self._wgpu_controller | ||
|
||
@property | ||
def wgpu_scene(self): | ||
return self._wgpu_scene | ||
|
||
@property | ||
def _viewport_size(self): | ||
return ( | ||
self.viewport().size().width() * 2, | ||
self.viewport().size().height() * 2, | ||
) | ||
|
||
def resizeEvent(self, event: QtGui.QResizeEvent) -> None: | ||
super().resizeEvent(event) | ||
|
||
self._wgpu_canvas.set_logical_size(*self._viewport_size) | ||
|
||
self.image_plane.setPixmap(self._render_to_pixmap()) | ||
|
||
def _render_to_pixmap(self): | ||
render = np.array(self._wgpu_renderer.target.draw())[..., :3] | ||
|
||
height, width, _channel = render.shape | ||
return QtGui.QPixmap.fromImage( | ||
QtGui.QImage( | ||
np.ascontiguousarray(render), | ||
width, | ||
height, | ||
3 * width, | ||
QtGui.QImage.Format_RGB888, | ||
) | ||
) | ||
|
||
def _mouse_event(self, event_type, event, touches=True): | ||
button = BUTTON_MAP.get(event.button(), 0) | ||
buttons = [ | ||
BUTTON_MAP[button] | ||
for button in BUTTON_MAP.keys() | ||
if button & event.buttons() | ||
] | ||
|
||
modifiers = [ | ||
MODIFIERS_MAP[mod] | ||
for mod in MODIFIERS_MAP.keys() | ||
if mod & event.modifiers() | ||
] | ||
|
||
wgpu_event = { | ||
"event_type": event_type, | ||
"x": event.pos().x(), | ||
"y": event.pos().y(), | ||
"button": button, | ||
"buttons": buttons, | ||
"modifiers": modifiers, | ||
} | ||
if touches: | ||
wgpu_event.update( | ||
{ | ||
"ntouches": 0, | ||
"touches": {}, | ||
} | ||
) | ||
|
||
self._wgpu_canvas.handle_event(wgpu_event) | ||
|
||
self.image_plane.setPixmap(self._render_to_pixmap()) | ||
|
||
def mousePressEvent(self, event): | ||
self._mouse_event("pointer_down", event) | ||
|
||
def mouseMoveEvent(self, event): | ||
self._mouse_event("pointer_move", event) | ||
|
||
def mouseReleaseEvent(self, event): | ||
self._mouse_event("pointer_up", event) | ||
|
||
def mouseDoubleClickEvent(self, event): | ||
self._mouse_event("double_click", event, touches=False) | ||
|
||
def wheelEvent(self, event): | ||
modifiers = [ | ||
MODIFIERS_MAP[mod] | ||
for mod in MODIFIERS_MAP.keys() | ||
if mod & event.modifiers() | ||
] | ||
|
||
wgpu_event = { | ||
"event_type": "wheel", | ||
"dx": -event.angleDelta().x(), | ||
"dy": -event.angleDelta().y(), | ||
"x": event.position().x(), | ||
"y": event.position().y(), | ||
"modifiers": modifiers, | ||
} | ||
|
||
self._wgpu_canvas.handle_event(wgpu_event) | ||
|
||
self.image_plane.setPixmap(self._render_to_pixmap()) | ||
|
||
|
||
def call_later(delay, callback, *args): | ||
QtCore.QTimer.singleShot(int(delay * 1000), lambda: callback(*args)) |