diff --git a/bLUeGui/colorPatterns.py b/bLUeGui/colorPatterns.py
index 48c94fc..5b0b7ce 100644
--- a/bLUeGui/colorPatterns.py
+++ b/bLUeGui/colorPatterns.py
@@ -17,7 +17,7 @@
"""
import numpy as np
from PySide6 import QtCore
-from PySide6.QtCore import QPoint, QLine, Qt, QPointF
+from PySide6.QtCore import Qt, QPointF, QLineF
from PySide6.QtWidgets import QLabel, QVBoxLayout, QWidget, QHBoxLayout
from bLUeTop.utils import QbLUeSlider
@@ -25,7 +25,6 @@
from PySide6.QtGui import QImage, QPixmap, QPainter, QPainterPath, QColor
from bLUeGui.bLUeImage import bImage, QImageBuffer
-from .graphicsForm import baseForm
class cmConverter(object):
@@ -51,9 +50,8 @@ def __init__(self):
class hueSatPattern(bImage):
"""
(hue, sat) color wheel image.
- For fast display, the correspondence with RGB values is tabulated for each brightness.
"""
- # hue rotation
+ # wheel rotation
rotation = 315
# default brightness
defaultBr = 0.45
@@ -62,7 +60,7 @@ def __init__(self, w, h, converter, bright=defaultBr, border=0):
"""
Builds a (hue, sat) color wheel image of size (w, h)
For fast display, the correspondence with RGB values is tabulated
- for each value of the brightness.
+ for each brightness.
:param w: image width
:type w: int
@@ -71,7 +69,7 @@ def __init__(self, w, h, converter, bright=defaultBr, border=0):
:param converter: color space converter
:type converter: cmConverter
:param bright: image brightness
- :type bright: int
+ :type bright: float
:param border: image border
:type border: int
"""
@@ -92,10 +90,10 @@ def __init__(self, w, h, converter, bright=defaultBr, border=0):
# init array of grid (cartesian) coordinates
coord = np.dstack(np.meshgrid(np.arange(w), - np.arange(h)))
- # center : i1 = i - cx, j1 = -j + cy
- cx = w / 2
- cy = h / 2
- coord = coord + [-cx, cy] # np.array([-cx, cy])
+ # wheel center : i1 = i - cx, j1 = -j + cy
+ self.center = QPointF(w, h) / 2
+ cx, cy = self.center.toTuple()
+ coord = coord + [-cx, cy]
# init hue and sat arrays as polar coordinates.
# arctan2 values are in range -pi, pi
@@ -104,6 +102,7 @@ def __init__(self, w, h, converter, bright=defaultBr, border=0):
hue = hue - np.floor(hue / 360.0) * 360.0
sat = np.linalg.norm(coord, axis=2, ord=2) / (cx - border)
np.minimum(sat, 1.0, out=sat)
+
# init a stack of image buffers, one for each brightness in integer range 0..100
hsBuf = np.dstack((hue, sat))[np.newaxis, :] # shape (1, h, w, 2)
hsBuf = np.tile(hsBuf, (101, 1, 1, 1)) # (101, h, w, 2)
@@ -142,12 +141,10 @@ def GetPoint(self, h, s):
:return:cartesian coordinates
:rtype: 2-uple of float
"""
- cx = self.width() / 2
- cy = self.height() / 2
+ cx, cy = self.center.toTuple()
x, y = (cx - self.border) * s * np.cos((h - self.rotation) * np.pi / 180.0), \
(cy - self.border) * s * np.sin((h - self.rotation) * np.pi / 180.0)
- x, y = x + cx, -y + cy
- return x, y
+ return x + cx, -y + cy
def GetPointVec(self, hsarray):
"""
@@ -161,12 +158,10 @@ def GetPointVec(self, hsarray):
:rtype: ndarray, shape=(w,h,2), dtype=float
"""
h, s = hsarray[:, :, 0], hsarray[:, :, 1]
- cx = self.width() / 2
- cy = self.height() / 2
+ cx, cy = self.center.toTuple()
x, y = (cx - self.border) * s * np.cos((h - self.rotation) * np.pi / 180.0), \
(cy - self.border) * s * np.sin((h - self.rotation) * np.pi / 180.0)
- x, y = x + cx, - y + cy
- return np.dstack((x, y))
+ return np.dstack((x + cx, -y + cy))
class brightnessPattern(bImage):
@@ -227,27 +222,36 @@ def __init__(self, w, h):
self.currentColor = QColor(255, 255, 255)
- cx, cy = int(w/2), int(h / 2)
- self.p = QPointF(cx, cy)
+ self.center = QPointF(w, h) / 2
+ cx, cy = self.center.toTuple()
+ self.p = QPointF(cx, cy) # copy
+
+ self.l1 = QLineF(cx - 5, cy, cx + 5, cy)
+ self.l2 = QLineF(cx, cy - 5, cx, cy + 5)
- self.l1 = QLine(cx - 5, cy, cx + 5, cy)
- self.l2 = QLine(cx, cy - 5, cx, cy + 5)
+ self.radius, self.theta = 0.0, 0.0 # used by mousePressEvent and mouseMoveEvent
+ self.w, self.h = w, h # used by paintEvent
self.qp = QPainter()
- self.path = QPainterPath()
- self.path.addEllipse(0.0, 0.0, w, h)
+ self.clPath = QPainterPath()
+ self.clPath.addEllipse(0.0, 0.0, w, h)
self.setPixmap(self.bareWheel)
def paintEvent(self, e):
self.qp.begin(self)
- self.qp.setClipPath(self.path)
+ self.qp.setClipPath(self.clPath)
self.qp.drawPixmap(0, 0, self.bareWheel)
+ self.qp.setPen(Qt.black)
+ # central crosshair
self.qp.drawLine(self.l1)
self.qp.drawLine(self.l2)
- self.qp.setPen(Qt.black)
- self.qp.drawEllipse(self.p, 5, 5)
+ # current radius
+ u = self.p - self.center
+ u *= max(self.w, self.h) / max(u.manhattanLength(), 0.001)
+ self.qp.drawLine(QLineF(self.center, self.p + u))
+ self.qp.drawEllipse(self.p, 6.0, 6.0)
self.qp.end()
def setCurrentColor(self, h, v):
@@ -266,14 +270,26 @@ def setCurrentColor(self, h, v):
def mousePressEvent(self, e):
p = e.position()
- self.p.setX(p.x())
- self.p.setY(p.y())
+ x, y = p.toTuple()
+ self.radius = np.sqrt((x - self.center.x()) ** 2 + (y - self.center.y()) ** 2)
+ self.theta = np.arctan2(-y + self.center.y(), x - self.center.x())
+ self.p.setX(x)
+ self.p.setY(y)
self.update()
def mouseMoveEvent(self, e):
p = e.position()
- self.p.setX(p.x())
- self.p.setY(p.y())
+ x, y = p.toTuple()
+ modifiers = e.modifiers()
+ if modifiers == Qt.ControlModifier:
+ # constant radius = self.radius
+ self.theta = np.arctan2(y - self.center.y(), x - self.center.x())
+ x, y = self.radius * np.cos(self.theta) + self.center.x(), self.radius * np.sin(self.theta) + self.center.y()
+ else:
+ self.theta = np.arctan2(y - self.center.y(), x - self.center.x())
+ self.radius = np.sqrt((x - self.center.x()) ** 2 + (y - self.center.y()) ** 2)
+ self.p.setX(x)
+ self.p.setY(y)
self.update()
def mouseReleaseEvent(self, e):
@@ -284,6 +300,7 @@ def update(self):
self.colorChanged.emit()
self.repaint()
+
class colorWheelChooser(QWidget):
"""
(Hue, Sat) color wheel picker, displaying a a slider and a sample of the current color.
diff --git a/bLUeTop/graphicsGrading.py b/bLUeTop/graphicsGrading.py
index 799ffe9..3c5b982 100644
--- a/bLUeTop/graphicsGrading.py
+++ b/bLUeTop/graphicsGrading.py
@@ -40,8 +40,6 @@ def getNewWindow(cls, targetImage=None, axeSize=500, LUTSize=LUTSIZE, layer=None
build a graphicsFormGrading object. The parameter axeSize represents the size of
the color wheel, border not included (the size of the window is adjusted).
- :param cModel: color Model converter
- :type cModel: cmConverter
:param targetImage
:type targetImage:
:param axeSize: size of the color wheel (default 500)
@@ -70,8 +68,7 @@ def getNewWindow(cls, targetImage=None, axeSize=500, LUTSize=LUTSIZE, layer=None
def __init__(self, targetImage=None, axeSize=500, LUTSize=LUTSIZE, layer=None, parent=None, mainForm=None):
"""
- :param cModel: color space used by colorPicker, slider2D and colorPicker
- :type cModel: cmConverter object
+
:param axeSize: size of the color wheel
:type axeSize: int
:param targetImage:
@@ -217,6 +214,7 @@ def g():
self.setWhatsThis(
"""Color Grading
Use shadows, midtones and highlights color wheels to pick the 3 corresponding colors.
+ For constant saturation, hold down the Ctrl key .
Use sliders to choose the shadows and highlights thresholds.
Your choices are reflected by the gradient image shown at the bottom
. Overlap between colors
is controlled by the overlap slider.
diff --git a/version.py b/version.py
index c4079f9..61d2bb6 100644
--- a/version.py
+++ b/version.py
@@ -16,4 +16,4 @@
along with this program. If not, see .
"""
-BLUE_VERSION = "V6-5.7.1"
+BLUE_VERSION = "V6-5.7.2"