Пример #1
0
 def onReset(self):
     """
     reset grid and LUT
     """
     # get a fresh LUT
     self.graphicsScene.lut = LUT3D(None, size=self.graphicsScene.lut.size, alpha=True)
     # explode all node groups
     groupList = [item for item in self.grid.childItems() if type(item) is nodeGroup]
     for item in groupList:
         item.prepareGeometryChange()
         self.scene().destroyItemGroup(item)
     # reset grid
     self.grid.reset()
     self.selected = None
     self.grid.drawGrid()
     self.layer.applyToStack()
     self.layer.parentImage.onImageChanged()
Пример #2
0
This File is part of bLUe software.

Copyright (C) 2017  Bernard Virot <*****@*****.**>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, version 3.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Lesser Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
#####################################
# Initializes LUT3D constants and objects
#####################################

import numpy as np
from bLUeCore.bLUeLUT3D import LUT3D

LUTSIZE = LUT3D.defaultSize
LUT3DIdentity = LUT3D(None, size=LUTSIZE)
LUTSTEP = LUT3DIdentity.step
LUT3D_ORI = LUT3DIdentity.LUT3DArray
a,b,c,d = LUT3D_ORI.shape
LUT3D_SHADOW = np.zeros((a,b,c,d+1))
LUT3D_SHADOW[:,:,:,:3] = LUT3D_ORI
Пример #3
0
    def __init__(self, cModel, 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:
        @type targetImage: imImage
        @param LUTSize:
        @type LUTSize: int
        @param layer: layer of targetImage linked to graphics form
        @type layer : QLayer
        @param parent:
        @type parent:
        """
        super().__init__(parent=parent)
        self.mainForm = mainForm  # used by saveLUT()
        # context help tag
        self.helpId = "LUT3DForm"
        self.cModel = cModel
        border = 20
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize + 90, axeSize + 200)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setBackgroundBrush(QBrush(Qt.black, Qt.SolidPattern))
        self.currentHue, self.currentSat, self.currentPb = 0, 0, self.defaultColorWheelBr
        self.currentR, self.currentG, self.currentB = 0, 0, 0
        self.size = axeSize
        # back links to image
        self.targetImage = weakProxy(targetImage)
        self.layer = weakProxy(layer)
        # currently selected grid node
        self.selected = None
        self.graphicsScene = QGraphicsScene()
        self.graphicsScene.options = None
        self.setScene(self.graphicsScene)
        # back to image layer
        self.graphicsScene.layer = weakProxy(layer)
        # init LUT
        freshLUT3D = LUT3D(None, size=LUTSize, alpha=True)
        self.graphicsScene.lut = freshLUT3D
        # init 2D slider
        QImg = hueSatPattern(axeSize, axeSize, cModel, bright=self.defaultColorWheelBr, border=border)
        self.graphicsScene.slider2D = colorChooser(cModel, QImg, target=self.targetImage, size=axeSize, border=border)
        self.graphicsScene.selectMarker = activeMarker.fromCross(parent=self.graphicsScene.slider2D)
        self.graphicsScene.selectMarker.setPos(axeSize / 2, axeSize / 2)
        # color wheel event handler
        def f1(p, r, g, b):
            h, s, br = self.cModel.rgb2cm(r, g, b)
            self.currentHue, self.currentSat, self.currentPb = h, s, br
            self.currentR, self.currentG, self.currentB = r, g, b
            self.bSliderUpdate()
            self.displayStatus()
        self.graphicsScene.slider2D.onMouseRelease = f1
        self.graphicsScene.addItem(self.graphicsScene.slider2D)

        # Brightness slider
        self.bSliderHeight = 20
        self.bSliderWidth = self.graphicsScene.slider2D.QImg.width()
        px = brightnessPattern(self.bSliderWidth, self.bSliderHeight, cModel, self.currentHue, self.currentSat).rPixmap
        self.graphicsScene.bSlider = QGraphicsPixmapItem(px, parent=self.graphicsScene.slider2D)
        self.graphicsScene.bSlider.setPos(QPointF(-border, self.graphicsScene.slider2D.QImg.height() - border))
        bSliderCursor = activeMarker.fromTriangle(parent=self.graphicsScene.bSlider)
        bSliderCursor.setMoveRange(QRectF(0.0, bSliderCursor.size, self.graphicsScene.bSlider.pixmap().width(), 0.0))
        bSliderCursor.setPos(self.graphicsScene.bSlider.pixmap().width() * self.defaultColorWheelBr, bSliderCursor.size)
        # cursor event handlers

        def f2(e, p, q):
            self.currentPb = p / float(self.bSliderWidth)
            QToolTip.showText(e.screenPos(), (str(int(self.currentPb * 100.0))), self)
        bSliderCursor.onMouseMove = f2

        def f3(e, p, q):
            self.currentPb = p / float(self.bSliderWidth)
            self.graphicsScene.slider2D.QImg.setPb(self.currentPb)
            self.graphicsScene.slider2D.setPixmap(self.graphicsScene.slider2D.QImg.rPixmap)
            self.displayStatus()
        bSliderCursor.onMouseRelease = f3
        # status bar
        offset = 60
        self.graphicsScene.statusBar = QGraphicsTextItem()
        self.graphicsScene.statusBar.setPos(-20, axeSize + offset)
        self.graphicsScene.statusBar.setDefaultTextColor(QColor(255, 255, 255))
        self.graphicsScene.statusBar.setPlainText('')
        self.graphicsScene.addItem(self.graphicsScene.statusBar)

        self.displayStatus()

        # self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)

        # grid
        self.grid = activeGrid(self.graphicsScene.lut.size, self.cModel, parent=self.graphicsScene.slider2D)
        self.graphicsScene.grid = self.grid

        # buttons
        pushButton1 = QbLUePushButton("Reset Grid")
        pushButton1.clicked.connect(self.onReset)
        pushButton2 = QbLUePushButton("Save LUT")
        pushButton2.clicked.connect(self.saveLUT)
        pushButton3 = QbLUePushButton("Smooth Grid")
        pushButton3.clicked.connect(self.onSmoothGrid)
        pushButton4 = QbLUePushButton('Set Mask')
        pushButton4.clicked.connect(self.setMask)
        # pushButton4 needs enabling/disabling
        self.pushButton4 = pushButton4

        # options
        options1, optionNames1 = ['use image', 'use selection'], ['Use Image', 'Use Selection']
        self.listWidget1 = optionsWidget(options=options1, optionNames=optionNames1, exclusive=True)
        """
        def onSelect1(item):
            self.graphicsScene.options['use selection'] = item is self.listWidget1.items['use selection']
        self.listWidget1.onSelect = onSelect1
        """
        self.listWidget1.setFocusPolicy(Qt.NoFocus)
        # set initial selection to 'use image'
        self.listWidget1.checkOption(self.listWidget1.intNames[0])

        options2, optionNames2 = ['add node', 'remove node'], ['Add Node', 'Remove Node']
        self.listWidget2 = optionsWidget(options=options2, optionNames=optionNames2, exclusive=True)
        """
        def onSelect2(item):
            self.graphicsScene.options['add node'] = item is self.listWidget2.items['add node']
        self.listWidget2.onSelect = onSelect2
        """
        # set initial selection to add node'
        self.listWidget2.checkOption(self.listWidget2.intNames[0])

        options3 = ['select neighbors', 'reset removed nodes', 'show histogram', 'keep alpha']
        optionNames3 = ['Select Neighbors', 'Reset Removed', 'Show Histogram', 'Keep Alpha']
        self.listWidget3 = optionsWidget(options=options3, optionNames=optionNames3, exclusive=False)
        self.listWidget3.checkOption(self.listWidget3.intNames[0])
        self.listWidget3.checkOption(self.listWidget3.intNames[1])

        def onSelect3(item):
            option = item.internalName
            if option == 'show histogram':
                self.graphicsScene.slider2D.showTargetHist = self.graphicsScene.options[option]  # .isChecked()
                self.graphicsScene.slider2D.updatePixmap()
                return
            if option == 'keep alpha':
                self.enableButtons()
                self.layer.applyToStack()

        self.listWidget3.onSelect = onSelect3
        # set initial selection to 'select naighbors'
        item = self.listWidget3.items[options3[0]]
        item.setCheckState(Qt.Checked)
        self.graphicsScene.options = UDict((self.listWidget1.options, self.listWidget2.options,
                                            self.listWidget3.options))
        # layouts
        hlButtons = QHBoxLayout()
        hlButtons.addWidget(pushButton1)
        hlButtons.addWidget(pushButton3)
        hlButtons.addWidget(pushButton2)
        hl = QHBoxLayout()
        vl1 = QVBoxLayout()
        vl1.addWidget(self.listWidget1)
        vl1.addWidget(pushButton4)
        hl.addLayout(vl1)
        hl.addWidget(self.listWidget2)
        hl.addWidget(self.listWidget3)
        vl = QVBoxLayout()
        for l in [hlButtons, hl]:
            vl.addLayout(l)
        # Init QWidget container for adding buttons and options to graphicsScene
        container = QWidget()
        container.setObjectName("container")
        container.setLayout(vl)
        ss = """QWidget#container{background: black}\
                QListWidget{background-color: black; selection-background-color: black; border: none; font-size: 7pt}\
                QListWidget::item{color: white;}\
                QListWidget::item::selected{background: black; border: none}"""
        container.setStyleSheet(ss)
        for wdg in [self.listWidget1, self.listWidget2, self.listWidget3]:
            wdg.setMinimumWidth(wdg.sizeHintForColumn(0))
            wdg.setMinimumHeight(wdg.sizeHintForRow(0)*len(wdg.items))
        container.setGeometry(-offset//2, axeSize + offset - 20, axeSize + offset, 20)
        self.graphicsScene.addWidget(container)

        #container.setStyleSheet("QPushButton {color: white;}\
         #                       QPushButton:pressed, QPushButton:hover {background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #0d5ca6, stop: 1 #2198c0);}")
        self.setWhatsThis(
""" <b>2.5D LUT Editor</b><br>
  HSpB layers are slower than HSV, but usually they give better results.<br>
  <b>Select nodes</b> with mouse clicks on the image. Selected nodes are shown
as small black circles on the color wheel.<br>
<b>Modify the color</b> of a node by dragging it on
the wheel. Several nodes can be moved simultaneously by grouping them.<br>
<b>Group nodes</b> :<br>
        &nbsp; 1 - select them with the mouse : while pressing the mouse left button, drag a rubber band around the nodes to select;<br>
        &nbsp; 2 - next, right click any one of the selected nodes and choose group from the context menu<br>
<b>unselect nodes</b> :<br>
        &nbsp; 1 - check the option Remove Node;<br>
        &nbsp; 2 -  ungroup;<br>
        &nbsp; 3 - on the image, click the pixels to unselect.<br>
<b>Caution</b> : Selecting nodes with the mouse is enabled only when
the Color Chooser is closed.<br>
Click the <b> Smooth Grid</b> button to smooth color transitions between neighbor nodes.<br>
Check the <br>Keep Alpha</b> option to forward the alpha channel without modifications.<br>
This option must be unchecked to build a mask from the 3D LUT.<br>
"""
                          )  # end of setWhatsThis
Пример #4
0
    def __init__(self, cModel, 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:
        @type targetImage: imImage
        @param LUTSize:
        @type LUTSize: int
        @param layer: layer of targetImage linked to graphics form
        @type layer : QLayer
        @param parent:
        @type parent:
        """
        super().__init__(targetImage=targetImage, layer=layer, parent=parent)
        self.mainForm = mainForm  # used by saveLUT()
        # context help tag
        self.helpId = "LUT3DForm"
        self.cModel = cModel
        border = 20
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize + 90, axeSize + 250)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setBackgroundBrush(QBrush(Qt.black, Qt.SolidPattern))
        self.currentHue, self.currentSat, self.currentPb = 0, 0, self.defaultColorWheelBr
        self.currentR, self.currentG, self.currentB = 0, 0, 0
        self.size = axeSize
        # currently selected grid node
        self.selected = None
        # init LUT
        freshLUT3D = LUT3D(None, size=LUTSize, alpha=True)
        self.graphicsScene.lut = freshLUT3D
        # init 2D slider (color wheel)
        swatchImg = hueSatPattern(axeSize, axeSize, cModel, bright=self.defaultColorWheelBr, border=border)
        slider2D = colorChooser(cModel, swatchImg, target=self.targetImage, size=axeSize, border=border)
        #########################################################################
        # CAUTION : sliedr2D has a non null offset
        # slider2D (and QImg) topleft corner is at scene pos -slider2D.offset()
        #########################################################################
        self.graphicsScene.addItem(slider2D)
        self.graphicsScene.slider2D = slider2D
        ##############################
        # neutral and working markers
        ##############################
        offset = slider2D.offset()
        neutralMarker = activeMarker.fromCross(parent=slider2D)
        neutralMarker.setPos(swatchImg.width() / 2 + offset.x(), swatchImg.height() / 2 + offset.y())
        self.workingMarker = activeMarker.fromCross(parent=slider2D)
        # default pos: average skin tone
        pt = QPointF(*swatchImg.GetPoint(*rgb2hsB(*cmyk2rgb(6, 25, 30, 0))[:2])) + offset
        self.workingMarker.setPos(pt.x(), pt.y())
        self.workingMarker.onMouseMove = lambda e, x, y: self.displayStatus()

        # swatch event handler
        def f1(p, r, g, b):
            h, s, br = self.cModel.rgb2cm(r, g, b)
            self.currentHue, self.currentSat, self.currentPb = h, s, br
            self.currentR, self.currentG, self.currentB = r, g, b
            self.bSliderUpdate()
            self.displayStatus()
        self.graphicsScene.slider2D.onMouseRelease = f1

        # Brightness slider
        self.bSliderHeight = 20
        self.bSliderWidth = self.graphicsScene.slider2D.QImg.width()
        px = brightnessPattern(self.bSliderWidth, self.bSliderHeight, cModel, self.currentHue, self.currentSat).rPixmap
        self.graphicsScene.bSlider = QGraphicsPixmapItem(px, parent=self.graphicsScene.slider2D)
        self.graphicsScene.bSlider.setPos(QPointF(-border, self.graphicsScene.slider2D.QImg.height() - border))
        bSliderCursor = activeMarker.fromTriangle(parent=self.graphicsScene.bSlider)
        bSliderCursor.setMoveRange(QRectF(0.0, bSliderCursor.size, self.graphicsScene.bSlider.pixmap().width(), 0.0))
        bSliderCursor.setPos(self.graphicsScene.bSlider.pixmap().width() * self.defaultColorWheelBr, bSliderCursor.size)

        # bSlider event handlers
        def f2(e, p, q):
            self.currentPb = p / float(self.bSliderWidth)
            self.graphicsScene.slider2D.QImg.setPb(self.currentPb)
            self.graphicsScene.slider2D.setPixmap(self.graphicsScene.slider2D.QImg.rPixmap)
            QToolTip.showText(e.screenPos(), (str(int(self.currentPb * 100.0))), self)
            
        bSliderCursor.onMouseMove = f2

        def f3(e, p, q):
            self.currentPb = p / float(self.bSliderWidth)
            # self.graphicsScene.slider2D.QImg.setPb(self.currentPb)
            # self.graphicsScene.slider2D.setPixmap(self.graphicsScene.slider2D.QImg.rPixmap)
            self.displayStatus()

        bSliderCursor.onMouseRelease = f3

        # grid
        self.grid = activeGrid(self.graphicsScene.lut.size, self.cModel, parent=self.graphicsScene.slider2D)
        self.graphicsScene.grid = self.grid

        # buttons
        pushButton1 = QbLUePushButton("Reset Grid")
        pushButton1.clicked.connect(self.onReset)
        pushButton2 = QbLUePushButton("Save LUT")
        pushButton2.clicked.connect(self.saveLUT)
        pushButton3 = QbLUePushButton("Smooth Grid")
        pushButton3.clicked.connect(self.onSmoothGrid)
        pushButton4 = QbLUePushButton('Set Mask')
        pushButton4.clicked.connect(self.setMask)
        # pushButton4 needs enabling/disabling
        self.pushButton4 = pushButton4

        # options
        options1, optionNames1 = ['use image', 'use selection'], ['Use Image', 'Use Selection']
        self.listWidget1 = optionsWidget(options=options1, optionNames=optionNames1, exclusive=True)

        self.listWidget1.setFocusPolicy(Qt.NoFocus)
        # set initial selection to 'use image'
        self.listWidget1.checkOption(self.listWidget1.intNames[0])

        options2, optionNames2 = ['add node', 'remove node'], ['Add Node', 'Remove Node']
        self.listWidget2 = optionsWidget(options=options2, optionNames=optionNames2, exclusive=True)

        # set initial selection to 'add node'
        self.listWidget2.checkOption(self.listWidget2.intNames[0])

        options3 = ['select neighbors', 'reset removed nodes', 'show histogram', 'keep alpha']
        optionNames3 = ['Select Neighbors', 'Reset Removed', 'Show Histogram', 'Keep Alpha']
        self.listWidget3 = optionsWidget(options=options3, optionNames=optionNames3, exclusive=False)
        self.listWidget3.checkOption(self.listWidget3.intNames[0])
        self.listWidget3.checkOption(self.listWidget3.intNames[1])

        def onSelect3(item):
            option = item.internalName
            if option == 'show histogram':
                self.graphicsScene.slider2D.showTargetHist = self.graphicsScene.options[option]
                self.graphicsScene.slider2D.updatePixmap()
                return
            if option == 'keep alpha':
                self.enableButtons()
                self.layer.applyToStack()

        self.listWidget3.onSelect = onSelect3
        # set initial selection to 'select naighbors'
        item = self.listWidget3.items[options3[0]]
        item.setCheckState(Qt.Checked)
        self.graphicsScene.options = UDict((self.listWidget1.options, self.listWidget2.options,
                                            self.listWidget3.options))

        for wdg in [self.listWidget1, self.listWidget2, self.listWidget3]:
            wdg.setMinimumWidth(wdg.sizeHintForColumn(0))
            wdg.setMinimumHeight(wdg.sizeHintForRow(0)*len(wdg.items))

        # color format combo
        infoCombo = QComboBox()
        oDict = OrderedDict([('RGB', 0), ('CMYK', 1), ('HSV', 2)])
        for key in oDict:
            infoCombo.addItem(key, oDict[key])

        labelFormat = QLabel()

        def colorInfoFormatChanged(value):
            self.colorInfoFormat = infoCombo.itemData(value)
            if self.colorInfoFormat == 0:
                labelFormat.setText('RGB')
            elif self.colorInfoFormat == 1:
                labelFormat.setText('CMYK')
            elif self.colorInfoFormat == 2:
                labelFormat.setText('HSV')
            self.displayStatus()

        infoCombo.currentIndexChanged.connect(colorInfoFormatChanged)

        # working marker position editor
        self.info = QLineEdit()
        self.info.setMaximumSize(120, 40)

        # returnPressed slot
        def infoDone():
            try:
                token = self.info.text().split(' ')
                if self.colorInfoFormat == 0:  # RGB
                    r, g, b = [int(x) for x in token if x.isdigit()]
                    pt = QPointF(*swatchImg.GetPoint(*rgb2hsB(r, g, b)[:2])) + offset
                elif self.colorInfoFormat == 1:  # CMYK
                    c, m, y, k = [int(x) for x in token if x.isdigit()]
                    pt = QPointF(*swatchImg.GetPoint(*rgb2hsB(*cmyk2rgb(c, m, y, k))[:2])) + offset
                elif self.colorInfoFormat == 2:  # HSV
                    h, s, _ = [int(x) for x in token if x.isdigit()]
                    if not 0 <= s <= 100:
                        raise ValueError
                    pt = QPointF(*swatchImg.GetPoint(h, s / 100.0)) + offset
                else:
                    raise ValueError
                self.workingMarker.setPos(pt.x(), pt.y())
            except ValueError:
                dlgWarn("Invalid color")

        self.info.returnPressed.connect(infoDone)

        # layout
        gl = QGridLayout()
        for i, button in enumerate([pushButton1, pushButton3, pushButton2]):
            gl.addWidget(button, 0, i)
        gl.addWidget(pushButton4, 1, 0)
        gl.addWidget(infoCombo, 1, 1)
        for i, widget in enumerate([self.listWidget1, self.listWidget2, self.listWidget3]):
            gl.addWidget(widget, 2 if i < 2 else 1, i, 1 if i < 2 else 2, 1, 0)
        hl = QHBoxLayout()
        hl.addWidget(labelFormat)
        hl.addWidget(self.info)
        gl.addLayout(hl, 3, 0, -1, -1)
        self.addCommandLayout(gl)

        # set defaults
        self.colorInfoFormat = 0  # RGB
        colorInfoFormatChanged(self.colorInfoFormat)

        # whatsthis
        self.setWhatsThis(
                        """ <b>2.5D LUT Editor</b><br>
                          <b>Select nodes</b> with mouse clicks on the image. Selected nodes are shown
                        as small black circles on the color wheel.<br>
                        <b>Modify the color</b> of a node by dragging it on
                        the wheel. Several nodes can be moved simultaneously by grouping them.<br>
                        <b>Group nodes</b> :<br>
                                &nbsp; 1 - group nodes with the mouse : while pressing the mouse left button,
                                           drag a rubber band around the nodes to group;<br>
                                &nbsp; 2 - next, right click any one of the selected nodes and 
                                choose group from the context menu<br>
                        <b>Unselect nodes</b> :<br>
                                &nbsp; 1 - check the option Remove Node;<br>
                                &nbsp; 2 -  ungroup;<br>
                                &nbsp; 3 - on the image, click the pixels to unselect.<br>
                        <b>Warning</b> : Selecting/unselecting nodes with the mouse is enabled only when
                        the Color Chooser is closed.<br>
                        Press the <b> Smooth Grid</b> button to smooth color transitions between neighbor nodes.<br>
                        <b>Crosshair</b> markers indicate neutral gray tones and average 
                        skin tones. They can be moved with the mouse.
                        The position of the second marker is reflected in the editable 
                        bottom right box. Due to inherent properties
                        of the CMYK color model, CMYK input values may be modified silently.<br>
                        <b>The grid can be zoomed</b> with the mouse wheel.<br>
                        Check the <b>Keep Alpha</b> option to forward the alpha channel without modifications.<br>
                        This option must be unchecked to build a mask from the 3D LUT.<br>
                        HSpB layer is slower than HSV, but usually gives better results.<br>    
                        """
                          )  # end of setWhatsThis