예제 #1
0
 def __init__(self, *args, **kwargs):
     ############################################################
     # Signals
     # Making QLayer inherit from QObject leads to
     # a bugged behavior of hasattr and getattr.
     # So, we don't add signals as first level class attributes.
     # Instead, we use instances of ad hoc signal containers.
     ############################################################
     self.visibilityChanged = baseSignal_bool()
     self.colorPicked = baseSignal_Int2()
     self.selectionChanged = baseSignal_No()
     self.maskSettingsChanged = baseSignal_No()
     ###########################################################
     # when a geometric transformation is applied to the whole image
     # each layer must be replaced with a transformed layer, recorded in tLayer
     # and tLayer.parentLayer keeps a reference to the original layer.
     ###########################################################
     self.tLayer = self
     self.parentLayer = self
     self.modified = False
     self.name = 'noname'
     self.visible = True
     self.isClipping = False
     self.role = kwargs.pop('role', '')
     self.tool = None
     # back link to parent image
     parentImage = kwargs.pop('parentImage', None)
     self.parentImage = weakProxy(parentImage)
     super().__init__(*args, **kwargs)  # don't move backwards
     # mask init, must be done after after calling super().__init__
     if type(self) not in [QPresentationLayer]:
         self.mask = QImage(self.width(), self.height(),
                            QImage.Format_ARGB32)
         # default : unmask all
         self.mask.fill(self.defaultColor_UnMasked)
     # layer opacity, range 0.0...1.0
     self.opacity = 1.0
     self.compositionMode = QPainter.CompositionMode_SourceOver
     # The next two attributes are used by adjustment layers only.
     self.execute = lambda l=None, pool=None: l.updatePixmap(
     ) if l is not None else None
     self.options = {}
     # actionName is used by methods graphics***.writeToStream()
     self.actionName = 'actionNull'
     # view is the dock widget containing
     # the graphics form associated with the layer
     self.view = None
     # undo/redo mask history
     self.historyListMask = historyList(size=5)
     # consecutive layers can be grouped.
     # A group is a list of QLayer objects
     self.group = []
     # layer offsets
     self.xOffset, self.yOffset = 0, 0
     self.Zoom_coeff = 1.0
     # clone dup layer shift and zoom  relative to current layer
     self.xAltOffset, self.yAltOffset = 0, 0
     self.AltZoom_coeff = 1.0
     self.updatePixmap()
예제 #2
0
 def __init__(self,
              targetImage=None,
              axeSize=500,
              layer=None,
              parent=None,
              mainForm=None):
     super().__init__(parent=parent)
     self.layer = layer
     # additional inactive curve to draw (QPolyLineF or list of QPointF)
     self.baseCurve = None
     self.setMinimumSize(axeSize + 60, axeSize + 140)
     self.setAttribute(Qt.WA_DeleteOnClose)
     graphicsScene = QGraphicsScene()
     self.setScene(graphicsScene)
     # back links to image
     graphicsScene.targetImage = weakProxy(targetImage)
     graphicsScene.layer = weakProxy(layer)
     graphicsScene.bgColor = QColor(200, 200, 200)
     self.mainForm = mainForm
     graphicsScene.axeSize = axeSize
     # add axes and grid
     graphicsScene.defaultAxes = self.drawPlotGrid(axeSize)
     graphicsScene.addItem(graphicsScene.defaultAxes)
     # connect layer colorPicked signal
     self.scene().layer.colorPicked.sig.connect(self.colorPickedSlot)
     # default WhatsThis for interactive curves
     self.setWhatsThis("""
         The background histogram is the <i>input</i> histogram; it is refreshed only
         when the curve is reset.<br>
         <b>Drag control points</b> with the mouse.<br>
         <b>Add a control point</b> by clicking on the curve.<br>
         <b>Remove a control point</b> by clicking it.<br>
         <b>Zoom</b> with the mouse wheel.<br>
         <b>Set black, white and neutral points</b> in the image by clicking the corresponding pixels
         while pressing one of the following key combination (RGB and Lab curves only):<br>
         &nbsp;&nbsp;<b>Black Point</b> : Ctrl+Shift<br>
         &nbsp;&nbsp;<b>White Point</b> : Ctrl<br>
         &nbsp;&nbsp;<b>Grey Neutral Point (Lab only)</b></br> : Shift<br>
         <b>Caution</b> : Selecting a black, white or neutral point in an image is enabled only when
         the Color Chooser is closed.
         """)  # end setWhatsThis
예제 #3
0
 def __init__(self, layer=None, targetImage=None, parent=None):
     super().__init__(parent=parent)  # QGraphicsView __init__ is mandatory : don't rely on MRO !
     self.setAlignment(Qt.AlignTop)
     self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
     self.setFocusPolicy(Qt.ClickFocus)
     # back links to image
     self.layer = layer
     self.targetImage = targetImage
     self.setScene(QGraphicsScene())
     # convenience attributes
     self.graphicsScene = weakProxy(self.scene())
     self.graphicsScene.options = None
     self.graphicsScene.layer = self.layer
     self.graphicsScene.targetImage = self.targetImage
     self.dataChanged.connect(self.updateLayer)
     # layer color picked signal
     if layer is not None:
         layer.colorPicked.sig.connect(self.colorPickedSlot)
예제 #4
0
    def setLayers(self, mImg, delete=False):
        """
        Displays the layer stack of mImg
        @param mImg: image
        @type mImg: mImage
        """
        # close open adjustment windows
        #self.closeAdjustForms()
        self.clear(delete=delete)
        mImg.layerView = self
        # back link to image
        self.img = weakProxy(mImg)
        model = layerModel()
        model.setColumnCount(3)
        l = len(mImg.layersStack)

        # dataChanged event handler : enables edition of layer name
        def f(index1, index2):
            # index1 and index2 should be equal
            # only layer name should be editable
            # dropEvent emit dataChanged when setting item values. f must
            # return immediately from these calls, as they are possibly made with unconsistent data :
            # dragged rows are already removed from layersStack
            # and not yet removed from model.
            if l != self.model().rowCount():
                return
            # only name is editable
            if index1.column() != 1:
                return
            row = index1.row()
            stackIndex = l - row - 1
            mImg.layersStack[stackIndex].name = index1.data()

        model.dataChanged.connect(f)
        for r, lay in enumerate(reversed(mImg.layersStack)):
            items = []
            # col 0 : visibility icon
            if lay.visible:
                item_visible = QStandardItem(
                    QIcon(":/images/resources/eye-icon.png"), "")
            else:
                item_visible = QStandardItem(
                    QIcon(":/images/resources/eye-icon-strike.png"), "")
            items.append(item_visible)
            # col 1 : image icon (for non-adjustment layeronly) and name
            if len(lay.name) <= 30:
                name = lay.name
            else:
                name = lay.name[:28] + '...'
            if hasattr(lay, 'inputImg'):
                item_name = QStandardItem(name)
            else:
                # icon with very small dim causes QPainter error
                # QPixmap.fromImage bug ?
                smallImg = lay.resize(50 * 50)
                w, h = smallImg.width(), smallImg.height()
                if w < h / 5 or h < w / 5:
                    item_name = QStandardItem(name)
                else:
                    item_name = QStandardItem(
                        QIcon(QPixmap.fromImage(smallImg)), name)
            # set tool tip to full name
            item_name.setToolTip(lay.name)
            items.append(item_name)
            item_mask = QStandardItem('m')
            items.append(item_mask)
            model.appendRow(items)
        self.setModel(model)
        self.horizontalHeader().hide()
        self.verticalHeader().hide()
        header = self.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        # select active layer
        self.selectRow(len(mImg.layersStack) - 1 - mImg.activeLayerIndex)
        layerview = mImg.getActiveLayer().view  # TODO added 25/11/18
        if layerview is not None:
            layerview.show()
            if TABBING:
                layerview.raise_()
        self.updateForm()
        for item in self.img.layersStack:
            if hasattr(item, 'sourceIndex'):
                combo = item.getGraphicsForm().sourceCombo
                currentText = combo.currentText()
                combo.clear()
                for i, x in enumerate(self.img.layersStack):
                    item.view.widget().sourceCombo.addItem(x.name, i)
                combo.setCurrentIndex(combo.findText(currentText))
예제 #5
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
예제 #6
0
 def targetImage(self, aTargetImage):
     self.__targetImage = weakProxy(aTargetImage)
예제 #7
0
 def layer(self, aLayer):
     self.__layer = weakProxy(aLayer)
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None):
        super().__init__(parent=parent)
        self.defaultFilterStart = 0
        self.defaultFilterEnd = 99
        self.filterStart = self.defaultFilterStart
        self.filterEnd = self.defaultFilterEnd

        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)

        # link back to image layer
        self.targetImage = weakProxy(targetImage)
        self.img = weakProxy(targetImage)
        self.layer = weakProxy(layer)
        """
        # using weak ref for back links
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        """
        self.mainForm = mainForm
        self.kernelCategory = filterIndex.UNSHARP
        self.kernel = getKernel(self.kernelCategory)
        # options
        optionList, optionNames = ['Gradual Top', 'Gradual Bottom'], ['Top To Bottom', 'Bottom To Top']
        filters = [blendFilterIndex.GRADUALTB, blendFilterIndex.GRADUALBT] #, blendFilterIndex.GRADUALNONE]
        filterDict = dict(zip(optionList, filters))

        self.listWidget1 = optionsWidget(options=optionList, optionNames=optionNames, exclusive=True, changed=self.dataChanged)
        # set initial selection to gradual top
        self.listWidget1.checkOption(optionList[0])

        rs = QRangeSlider()
        rs.setMaximumSize(16000, 10)

        rs.tail.setStyleSheet(
            'background: white; /*qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #222, stop:1 #888); margin 3px;*/')
        rs.handle.setStyleSheet('background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 black, stop:1 white);')
        rs.head.setStyleSheet(
            'background: black; /*qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #999, stop:1 #222);*/')
        self.sliderFilterRange = rs
        frLabel = QLabel('Range')

        # filter range done event handler
        def frUpdate(start, end):
            if self.sliderFilterRange.isSliderDown() or (start == self.filterStart and end == self.filterEnd):
                return
            self.sliderFilterRange.startValueChanged.disconnect()
            self.sliderFilterRange.endValueChanged.disconnect()
            self.sliderFilterRange.rangeDone.disconnect()
            self.filterStart, self.filterEnd = self.sliderFilterRange.getRange()
            self.dataChanged.emit()
            self.sliderFilterRange.startValueChanged.connect(frUpdate)  # send new value as parameter
            self.sliderFilterRange.endValueChanged.connect(frUpdate)  # send new value as parameter
            self.sliderFilterRange.rangeDone.connect(frUpdate)

        self.sliderFilterRange.startValueChanged.connect(frUpdate)  # send new value as parameter
        self.sliderFilterRange.endValueChanged.connect(frUpdate)  # send new value as parameter
        self.sliderFilterRange.rangeDone.connect(frUpdate)

        # data changed event handler
        def updateLayer():
            #enableSliders()
            for key in self.listWidget1.options:
                if self.listWidget1.options[key]:
                    self.kernelCategory = filterDict[key]
                    break
            self.layer.applyToStack()
            self.layer.parentImage.onImageChanged()

        self.dataChanged.connect(updateLayer)  # TODO 3/12/18 move to setDefaults
        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignBottom)
        l.addWidget(self.listWidget1)
        hl8 = QHBoxLayout()
        hl8.addWidget(frLabel)
        hl8.addWidget(self.sliderFilterRange)
        l.addLayout(hl8)
        l.setContentsMargins(20, 0, 20, 25)  # left, top, right, bottom
        self.setLayout(l)

        self.setWhatsThis(
"""<b>Gradual neutral filter.</b><br> 
   It mimics the classical gradual gray filter often used by photographers to darken the sky.<br>
   To control the regions of maximum and minimum intensities use the Range slider.
"""
                        ) # end setWhatsThis

        self.setDefaults()
예제 #9
0
    def __init__(self,
                 targetImage=None,
                 size=200,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(parent=parent)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.setMinimumSize(size, 100)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.targetImage = weakProxy(targetImage)
        self.img = weakProxy(targetImage)
        # link back to image layer
        # using weak ref for back links
        self.layer = layer if (layer is None or type(layer)
                               in weakref.ProxyTypes) else weakref.proxy(layer)
        self.Label_Hist = QLabel()
        self.Label_Hist.setScaledContents(True)
        self.Label_Hist.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Preferred)
        #self.setStyleSheet("QListWidget{background-color: rgb(200,200,200); selection-background-color: rgb(200,200,200); border: 0px; font-size: 9px}")
        self.setStyleSheet("QListWidget{border: 0px; font-size: 12px}")

        # options. We want the items displayed horizontally, so we use 2 lists
        options1, optionNames1 = ['Original Image'], ['Source']
        self.listWidget1 = optionsWidget(options=options1,
                                         optionNames=optionNames1,
                                         exclusive=False)
        self.listWidget1.item(0).setSizeHint(QSize(100, 10))
        self.listWidget1.setMaximumSize(
            self.listWidget1.sizeHintForColumn(0) + 5,
            self.listWidget1.sizeHintForRow(0) * len(options1))

        options2 = ['Color Chans']
        self.listWidget2 = optionsWidget(options=options2, exclusive=False)
        self.listWidget2.item(0).setSizeHint(QSize(100, 10))
        self.listWidget2.setMaximumSize(
            self.listWidget2.sizeHintForColumn(0) + 5,
            self.listWidget2.sizeHintForRow(0) * len(options2))
        # default: show color hists
        self.listWidget2.item(0).setCheckState(Qt.Checked)

        self.options = {option: True for option in options1 + options2}

        def onSelect1(item):
            self.options[options1[0]] = item.checkState() is Qt.Checked
            try:
                self.targetImage.onImageChanged()
                self.Label_Hist.update()
            except AttributeError:
                return

        def onSelect2(item):
            self.options[options2[0]] = item.checkState() is Qt.Checked
            try:
                self.targetImage.onImageChanged()
                self.Label_Hist.update()
            except AttributeError:
                return

        self.listWidget1.onSelect = onSelect1
        self.listWidget2.onSelect = onSelect2

        # layout
        h = QHBoxLayout()
        h.addWidget(self.listWidget1)
        h.addWidget(self.listWidget2)
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignTop)
        l.addWidget(self.Label_Hist)
        l.addLayout(h)
        l.setContentsMargins(0, 0, 0, 2)  # left, top, right, bottom
        self.setLayout(l)
예제 #10
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super(transForm, self).__init__(parent=parent)

        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # back links to image
        self.targetImage = weakProxy(targetImage)
        self.img = weakProxy(targetImage)
        self.layer = weakProxy(layer)
        self.mainForm = mainForm
        # options
        optionList1, optionNames1 = ['Free', 'Rotation', 'Translation'], [
            'Free Transformation', 'Rotation', 'Translation'
        ]
        self.listWidget1 = optionsWidget(options=optionList1,
                                         optionNames=optionNames1,
                                         exclusive=True)
        optionList2, optionNames2 = ['Transparent'
                                     ], ['Set Transparent Pixels To Black']
        self.listWidget2 = optionsWidget(options=optionList2,
                                         optionNames=optionNames2,
                                         exclusive=False)
        self.options = UDict(
            (self.listWidget1.options, self.listWidget2.options))
        # set initial selection to Perspective
        self.listWidget1.checkOption(optionList1[0])

        # option changed handler
        def g(item):
            l = self.layer
            l.tool.setBaseTransform()
            #self.layer.tool.setVisible(True)
            l.applyToStack()
            l.parentImage.onImageChanged()

        self.listWidget1.onSelect = g
        self.listWidget2.onSelect = g
        pushButton1 = QPushButton(' Reset Transformation ')
        pushButton1.adjustSize()

        def f():
            self.layer.tool.resetTrans()

        pushButton1.clicked.connect(f)
        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignTop)
        l.addWidget(self.listWidget1)
        l.addWidget(self.listWidget2)
        hl = QHBoxLayout()
        hl.setAlignment(Qt.AlignHCenter)
        hl.addWidget(pushButton1)
        l.addLayout(hl)
        self.setLayout(l)
        self.adjustSize()
        self.setWhatsThis("""
<b>Geometric transformation :</b><br>
  Choose a transformation type and drag either corner of the image using the small square red buttons.<br>
  Ctrl+Alt+Drag to change the <b>initial positions</b> of buttons.
""")
예제 #11
0
    def __init__(self, layer=None):
        super(segmentForm, self).__init__()
        self.setWindowTitle('grabcut')
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(200, 200)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # link back to image layer
        self.layer = weakProxy(layer)
        pushButton = QPushButton('apply')
        # button slot
        def f():
            self.layer.noSegment = False
            self.layer.applyToStack()
            window.label.img.onImageChanged()
            # do manual segmentation only
            layer.noSegment = True
        pushButton.clicked.connect(f)

        pushButton1 = QPushButton('Reset')
        pushButton1.clicked.connect(lambda : self.reset())

        self.spBox = QSpinBox()
        self.spBox.setRange(1,10)
        # spBox Slot
        def f2(iterCount):
            self.spBox.valueChanged.disconnect()
            self.dataChanged.emit()
            self.spBox.valueChanged.connect(f2)
        self.spBox.valueChanged.connect(f2)
        spBoxLabel = QLabel()
        spBoxLabel.setText('Iterations')

        self.spBox1 = QSpinBox()
        self.spBox1.setRange(0, 20)
        spBox1Label = QLabel()
        spBox1Label.setText('Contour Margin')
        # spBox1 slot
        def f1(margin):
            self.spBox1.valueChanged.disconnect()
            self.dataChanged.emit()
            self.spBox1.valueChanged.connect(f1)
        self.spBox1.valueChanged.connect(f1)

        # options
        optionList1, optionNames1 = ['Clipping Layer'], ['Clipping Layer']
        self.listWidget1 = optionsWidget(options=optionList1, optionNames=optionNames1, exclusive=False)
        self.options = self.listWidget1.options
        # option changed slot
        def g(item):
            self.layer.isClipping = self.options['Clipping Layer']
            self.layer.applyToStack()
            self.layer.parentImage.onImageChanged()
        self.listWidget1.onSelect = g

        # attributes initialized in setDefaults, declared here
        # for the sake of correctness
        self.start = None
        self.nbIter = None
        self.contourMargin = None

        # layout
        hLay = QHBoxLayout()
        hLay.addWidget(spBoxLabel)
        hLay.addWidget(self.spBox)
        hLay.addStretch(1)
        hLay1 = QHBoxLayout()
        hLay1.addWidget(spBox1Label)
        hLay1.addWidget(self.spBox1)
        hLay1.addStretch(1)
        h2 = QHBoxLayout()
        h2.addWidget(self.listWidget1)
        vLay = QVBoxLayout()
        vLay.setAlignment(Qt.AlignTop)
        vLay.setContentsMargins(20, 8, 20, 25)  # left, top, right, bottom
        vLay.addLayout(hLay)
        vLay.addLayout(hLay1)
        vLay.addLayout(h2)
        h3 = QHBoxLayout()
        h3.addWidget(pushButton)
        h3.addWidget(pushButton1)
        vLay.addLayout(h3)
        self.setLayout(vLay)
        self.setDefaults()
        self.setWhatsThis(
""" <b>Object extraction</b><br>  
  Select the object to extract with the rectangle Marquee Tool. Next, click the Apply button.<br>
  Correct (roughly) if needed the foreground (FG) and the background (BG) regions using the FG and BG tools (Ctrl to undo) and click again the Apply button.<br>
  To get a smoother contour increase the value of the Contour Margin and click the Apply Button.<br>
  By default the mask is displayed as a color mask. To view it as an opacity mask, right click on the Segmentation layer row in the right pane and check Enable Mask As > Opacity Mask in the context menu.
  Use the same context menu to copy/paste the object to a new image layer or the mask to another layer.<br>
  
"""
                        )  # end setWhatsThis
예제 #12
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(parent=parent)
        defaultRadius = 10
        defaultTone = 100.0
        defaultAmount = 50.0
        self.radius = defaultRadius
        self.tone = defaultTone
        self.amount = defaultAmount
        self.targetImage = targetImage
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.img = targetImage
        # link back to image layer
        self.layer = weakProxy(layer)
        """
        # using weak ref for back links
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        """
        self.mainForm = mainForm
        self.kernelCategory = filterIndex.UNSHARP

        # options
        self.optionList = [
            'Unsharp Mask', 'Sharpen', 'Gaussian Blur', 'Surface Blur'
        ]
        filters = [
            filterIndex.UNSHARP, filterIndex.SHARPEN, filterIndex.BLUR1,
            filterIndex.SURFACEBLUR
        ]
        self.filterDict = dict(zip(self.optionList, filters))

        self.listWidget1 = optionsWidget(options=self.optionList,
                                         exclusive=True,
                                         changed=self.dataChanged)
        # set initial selection to unsharp mask
        item = self.listWidget1.checkOption(self.optionList[0])

        # sliders
        self.sliderRadius = QbLUeSlider(Qt.Horizontal)
        #self.sliderRadius.setTickPosition(QSlider.TicksBelow)
        self.sliderRadius.setRange(1, 50)
        self.sliderRadius.setSingleStep(1)
        self.radiusLabel = QLabel()
        self.radiusLabel.setMaximumSize(150, 30)
        self.radiusLabel.setText("Radius")

        self.radiusValue = QLabel()
        font = self.radiusValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("1000 ")
        h = metrics.height()
        self.radiusValue.setMinimumSize(w, h)
        self.radiusValue.setMaximumSize(w, h)

        self.sliderAmount = QbLUeSlider(Qt.Horizontal)
        #self.sliderAmount.setTickPosition(QSlider.TicksBelow)
        self.sliderAmount.setRange(0, 100)
        self.sliderAmount.setSingleStep(1)
        self.amountLabel = QLabel()
        self.amountLabel.setMaximumSize(150, 30)
        self.amountLabel.setText("Amount")
        self.amountValue = QLabel()
        font = self.radiusValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("1000 ")
        h = metrics.height()
        self.amountValue.setMinimumSize(w, h)
        self.amountValue.setMaximumSize(w, h)

        self.toneValue = QLabel()
        self.toneLabel = QLabel()
        self.toneLabel.setMaximumSize(150, 30)
        self.toneLabel.setText("Sigma")
        self.sliderTone = QbLUeSlider(Qt.Horizontal)
        #self.sliderTone.setTickPosition(QSlider.TicksBelow)
        self.sliderTone.setRange(0, 100)
        self.sliderTone.setSingleStep(1)
        font = self.radiusValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("1000 ")
        h = metrics.height()
        self.toneValue.setMinimumSize(w, h)
        self.toneValue.setMaximumSize(w, h)

        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignBottom)
        l.addWidget(self.listWidget1)
        hl = QHBoxLayout()
        hl.addWidget(self.radiusLabel)
        hl.addWidget(self.radiusValue)
        hl.addWidget(self.sliderRadius)
        l.addLayout(hl)
        hl = QHBoxLayout()
        hl.addWidget(self.amountLabel)
        hl.addWidget(self.amountValue)
        hl.addWidget(self.sliderAmount)
        l.addLayout(hl)
        hl = QHBoxLayout()
        hl.addWidget(self.toneLabel)
        hl.addWidget(self.toneValue)
        hl.addWidget(self.sliderTone)
        l.addLayout(hl)
        l.setContentsMargins(20, 0, 20, 25)  # left, top, right, bottom
        self.setLayout(l)

        # value changed event handler
        def sliderUpdate():
            self.radiusValue.setText(str('%d ' % self.sliderRadius.value()))
            self.amountValue.setText(str('%d ' % self.sliderAmount.value()))
            self.toneValue.setText(str('%d ' % self.sliderTone.value()))

        # value done event handler
        def formUpdate():
            sR, sA, sT = self.sliderRadius.isEnabled(
            ), self.sliderAmount.isEnabled(), self.sliderTone.isEnabled()
            self.sliderRadius.setEnabled(False)
            self.sliderAmount.setEnabled(False)
            self.sliderTone.setEnabled(False)
            self.tone = self.sliderTone.value()
            self.radius = self.sliderRadius.value()
            self.amount = self.sliderAmount.value()
            sliderUpdate()
            self.dataChanged.emit()
            self.sliderRadius.setEnabled(sR)
            self.sliderAmount.setEnabled(sA)
            self.sliderTone.setEnabled(sT)

        self.sliderRadius.valueChanged.connect(sliderUpdate)
        self.sliderRadius.sliderReleased.connect(formUpdate)
        self.sliderAmount.valueChanged.connect(sliderUpdate)
        self.sliderAmount.sliderReleased.connect(formUpdate)
        self.sliderTone.valueChanged.connect(sliderUpdate)
        self.sliderTone.sliderReleased.connect(formUpdate)

        self.dataChanged.connect(self.updateLayer)
        # init
        self.sliderRadius.setValue(defaultRadius)
        self.sliderAmount.setValue(defaultAmount)
        self.sliderTone.setValue(defaultTone)
        self.enableSliders()
        sliderUpdate()
        self.setWhatsThis("""
   <b>Unsharp Mask</b> and <b>Sharpen Mask</b> are used to sharpen an image.<br>
   <b>Gaussian Blur</b> and <b>Surface Blur</b> are used to blur an image.<br>
   In contrast to Gaussian Blur, Surface Blur preserves edges and reduces noise.<br>
   It is possible to <b>limit the effect of a filter to a rectangular region of the image</b> by
   drawing a selection rectangle on the layer with the marquee tool.<br>
   Ctrl-Click to <b>clear the selection</b><br>
   
""")  # end setWhatsThis
예제 #13
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(parent=parent)
        self.tempCorrection = 6500
        self.tintCorrection = 1.0
        self.setStyleSheet(
            'QRangeSlider * {border: 0px; padding: 0px; margin: 0px}')
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # link back to image layer
        self.layer = weakProxy(layer)
        """
        # using weak ref for back links
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        """
        self.defaultTemp = sRGBWP  # ref temperature D65
        self.defaultTint = 0

        # options
        optionList, optionNames = ['Photo Filter', 'Chromatic Adaptation'
                                   ], ['Photo Filter', 'Chromatic Adaptation']
        self.listWidget1 = optionsWidget(
            options=optionList,
            optionNames=optionNames,
            exclusive=True,
            changed=lambda: self.dataChanged.emit())
        self.listWidget1.checkOption(self.listWidget1.intNames[1])
        self.options = self.listWidget1.options

        # temp slider
        self.sliderTemp = QbLUeSlider(Qt.Horizontal)
        self.sliderTemp.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultIColorStylesheet)
        self.sliderTemp.setRange(
            17, 100
        )  # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP
        self.sliderTemp.setSingleStep(1)

        tempLabel = QbLUeLabel()
        tempLabel.setMaximumSize(150, 30)
        tempLabel.setText("Color temperature")
        tempLabel.doubleClicked.connect(lambda: self.sliderTemp.setValue(
            self.temp2Slider(self.defaultTemp)))

        self.tempValue = QLabel()
        font = self.tempValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("0000")
        h = metrics.height()
        self.tempValue.setMinimumSize(w, h)
        self.tempValue.setMaximumSize(w, h)
        self.tempValue.setText(
            str("{:d}".format(self.sliderTemp2User(self.sliderTemp.value()))))

        # tint slider
        self.sliderTint = QbLUeSlider(Qt.Horizontal)
        self.sliderTint.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultMGColorStylesheet)
        self.sliderTint.setRange(
            0, 100
        )  # 250) # valid range for spline approximation is 1667..25000, cf. colorConv.temperature2xyWP
        self.sliderTint.setSingleStep(1)

        tintLabel = QbLUeLabel()
        tintLabel.setMaximumSize(150, 30)
        tintLabel.setText("Tint")
        tintLabel.doubleClicked.connect(lambda: self.sliderTint.setValue(
            self.tint2Slider(self.defaultTint)))

        self.tintValue = QLabel()
        font = self.tintValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("0000")
        h = metrics.height()
        self.tintValue.setMinimumSize(w, h)
        self.tintValue.setMaximumSize(w, h)
        self.tintValue.setText(
            str("{:d}".format(self.sliderTint2User(self.sliderTint.value()))))

        # temp change event handler
        def tempUpdate(value):
            self.tempValue.setText(
                str("{:d}".format(self.sliderTemp2User(value))))
            # move not yet terminated or values not modified
            if self.sliderTemp.isSliderDown() or self.slider2Temp(
                    value) == self.tempCorrection:
                return
            self.sliderTemp.valueChanged.disconnect()
            self.sliderTemp.sliderReleased.disconnect()
            self.tempCorrection = self.slider2Temp(value)
            self.dataChanged.emit()
            self.sliderTemp.valueChanged.connect(tempUpdate)
            self.sliderTemp.sliderReleased.connect(
                lambda: tempUpdate(self.sliderTemp.value()))

        # tint change event handler
        def tintUpdate(value):
            self.tintValue.setText(
                str("{:d}".format(self.sliderTint2User(value))))
            # move not yet terminated or values not modified
            if self.sliderTint.isSliderDown() or self.slider2Tint(
                    value) == self.tintCorrection:
                return
            self.sliderTint.valueChanged.disconnect()
            self.sliderTint.sliderReleased.disconnect()
            self.tintCorrection = self.slider2Tint(value)
            self.dataChanged.emit()
            self.sliderTint.valueChanged.connect(tintUpdate)
            self.sliderTint.sliderReleased.connect(
                lambda: tintUpdate(self.sliderTint.value()))

        self.sliderTemp.valueChanged.connect(tempUpdate)
        self.sliderTemp.sliderReleased.connect(
            lambda: tempUpdate(self.sliderTemp.value()))
        self.sliderTint.valueChanged.connect(tintUpdate)
        self.sliderTint.sliderReleased.connect(
            lambda: tintUpdate(self.sliderTint.value()))

        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignTop)
        l.addWidget(self.listWidget1)
        l.addWidget(tempLabel)
        hl = QHBoxLayout()
        hl.addWidget(self.tempValue)
        hl.addWidget(self.sliderTemp)
        l.addLayout(hl)
        l.addWidget(tintLabel)
        hl1 = QHBoxLayout()
        hl1.addWidget(self.tintValue)
        hl1.addWidget(self.sliderTint)
        l.addLayout(hl1)
        self.setLayout(l)
        self.adjustSize()
        self.dataChanged.connect(
            self.updateLayer)  # TODO move to setDefaults 3/12/18
        self.setStyleSheet("QListWidget, QLabel {font : 7pt;}")
        self.setDefaults()
        self.setWhatsThis("""<b>Color Temperature</b><br>
<b>Photo Filter</b> uses the multiply blending mode to mimic a color filter
in front of the camera lens.<br>
<b>Chromatic Adaptation</b> uses multipliers in the linear sRGB
color space to adjust <b>temperature</b> and <b>tint</b>.
""")  # end of setWhatsThis
예제 #14
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(parent=parent)
        self.setStyleSheet(
            'QRangeSlider * {border: 0px; padding: 0px; margin: 0px}')
        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize +
                            300)  # +300 to prevent scroll bars in list Widgets
        self.setAttribute(Qt.WA_DeleteOnClose)
        # links back to image : using weak refs
        self.targetImage = weakProxy(targetImage)
        """
        if type(targetImage) in weakref.ProxyTypes:
            self.targetImage = targetImage           
        else:
            self.targetImage = weakref.proxy(targetImage)
        """
        #self.targetImage = targetImage
        self.layer = weakProxy(layer)
        """
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        """
        #######################################
        # Libraw correspondences:
        # rgb_xyz_matrix is libraw cam_xyz
        # camera_whitebalance is libraw cam_mul
        # daylight_whitebalance is libraw pre_mul
        # dng correspondences:
        # ASSHOTNEUTRAL tag value is (X,Y,Z) =  1 / rawpyObj.camera_whitebalance
        ##########################################
        rawpyObj = layer.parentImage.rawImage

        # constants and as shot values
        self.XYZ2CameraMatrix = rawpyObj.rgb_xyz_matrix[:3, :]
        self.XYZ2CameraInverseMatrix = np.linalg.inv(self.XYZ2CameraMatrix)
        # initial post processing multipliers (as shot)
        m1, m2, m3, m4 = rawpyObj.camera_whitebalance
        self.asShotMultipliers = (
            m1 / m2, 1.0, m3 / m2, m4 / m2
        )  # normalization is mandatory : for nef files white balance is around 256
        self.asShotTemp, self.asShotTint = multipliers2TemperatureAndTint(
            *1 / np.array(self.asShotMultipliers[:3]), self.XYZ2CameraMatrix)
        self.rawMultipliers = self.asShotMultipliers  #rawpyObj.camera_whitebalance # = 1/(dng ASSHOTNEUTRAL tag value)
        self.sampleMultipliers = False
        self.samples = []
        ########################################
        # XYZ-->Camera conversion matrix:
        # Last row is zero for RGB cameras (cf. rawpy and libraw docs).
        # type ndarray, shape (4,3)
        #########################################

        # attributes initialized in setDefaults, declared here for the sake of correctness
        self.tempCorrection, self.tintCorrection, self.expCorrection, self.highCorrection,\
                                                   self.contCorrection, self.satCorrection, self.brCorrection = [None] * 7
        # contrast spline vie (initialized by setContrastSpline)
        self.contrastForm = None
        # tone spline view (initialized by setToneSpline)
        self.toneForm = None
        # dock containers for contrast and tome forms
        self.dockC, self.dockT = None, None
        # options
        optionList0, optionNames0 = ['Auto Brightness', 'Preserve Highlights'
                                     ], ['Auto Expose', 'Preserve Highlights']
        optionList1, optionNames1 = ['Auto WB', 'Camera WB', 'User WB'
                                     ], ['Auto', 'Camera (As Shot)', 'User']
        optionList2, optionNames2 = [
            'cpLookTable', 'cpToneCurve', 'manualCurve'
        ], [
            'Use Camera Profile Look Table', 'Show Tone Curves',
            'Show Contrast Curve'
        ]
        self.listWidget1 = optionsWidget(
            options=optionList0,
            optionNames=optionNames0,
            exclusive=False,
            changed=lambda: self.dataChanged.emit(1))
        #self.listWidget1.checkOption(self.listWidget1.intNames[0])
        #self.listWidget1.checkOption(self.listWidget1.intNames[1])
        self.listWidget2 = optionsWidget(
            options=optionList1,
            optionNames=optionNames1,
            exclusive=True,
            changed=lambda: self.dataChanged.emit(1))
        #self.listWidget2.checkOption(self.listWidget2.intNames[1])
        self.listWidget3 = optionsWidget(
            options=optionList2,
            optionNames=optionNames2,
            exclusive=False,
            changed=lambda: self.dataChanged.emit(2))
        self.options = UDict(
            (self.listWidget1.options, self.listWidget2.options,
             self.listWidget3.options))
        # display the 'as shot' temperature
        item = self.listWidget2.item(1)
        item.setText(item.text() + ' : %d' % self.asShotTemp)

        # temperature slider
        self.sliderTemp = QbLUeSlider(Qt.Horizontal)
        self.sliderTemp.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultColorStylesheet)
        self.sliderTemp.setRange(
            0, 100)  # TODO 130 changed to 100 12/11/18 validate
        self.sliderTemp.setSingleStep(1)

        self.tempLabel = QLabel()
        self.tempLabel.setText("Temp")

        self.tempValue = QLabel()
        font = self.tempValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("10000")
        h = metrics.height()
        self.tempValue.setMinimumSize(w, h)
        self.tempValue.setMaximumSize(w, h)
        self.tempValue.setText(
            str("{:.0f}".format(self.slider2Temp(self.sliderTemp.value()))))

        self.sliderTemp.valueChanged.connect(
            self.tempUpdate)  # send new value as parameter
        self.sliderTemp.sliderReleased.connect(lambda: self.tempUpdate(
            self.sliderTemp.value()))  # signal has no parameter

        # tint slider
        self.sliderTint = QbLUeSlider(Qt.Horizontal)
        # self.sliderTint.setStyleSheet(self.sliderTint.styleSheet()+'QSlider::groove:horizontal {background: red;}')
        self.sliderTint.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultIMGColorStylesheet)
        self.sliderTint.setRange(0, 150)

        self.sliderTint.setSingleStep(1)

        self.tintLabel = QLabel()
        self.tintLabel.setText("Tint")

        self.tintValue = QLabel()
        font = self.tempValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("100")
        h = metrics.height()
        self.tintValue.setMinimumSize(w, h)
        self.tintValue.setMaximumSize(w, h)
        self.tintValue.setText(
            str("{:.0f}".format(self.sliderTint2User(
                self.sliderTint.value()))))

        self.sliderTint.valueChanged.connect(self.tintUpdate)
        self.sliderTint.sliderReleased.connect(lambda: self.tintUpdate(
            self.sliderTint.value()))  # signal has no parameter)

        ######################
        # From libraw and dcraw sources:
        # Exposure and brightness are curve transformations.
        # Exposure curve is y = alpha*x, with cubic root ending; it is applied before demosaicing.
        # Brightness is (similar to) y = x**alpha and part of gamma transformation from linear sRGB to RGB.
        # Exposure and brightness both dilate the histogram towards highlights.
        # Exposure dilatation is uniform (homothety), brightness dilataion is
        # maximum for the midtones and the highlghts are preserved.
        # As a consequence, normal workflow begins with the adjustment of exposure,
        # to fill the entire range of the histogram and to adjust the highlights. Next,
        # one adjusts the brightness to put the midtones at the level we want them to be.
        # Cf. https://www.cambridgeincolour.com/forums/thread653.htm
        #####################

        # profile combo
        self.dngDict = self.setCameraProfilesCombo()

        # cameraProfilesCombo index changed event handler
        def cameraProfileUpdate(value):
            self.dngDict = self.cameraProfilesCombo.itemData(value)
            if self.options['cpToneCurve']:
                toneCurve = dngProfileToneCurve(
                    self.dngDict.get('ProfileToneCurve', []))
                self.toneForm.baseCurve = [
                    QPointF(x * axeSize, -y * axeSize)
                    for x, y in zip(toneCurve.dataX, toneCurve.dataY)
                ]
                self.toneForm.update()
            # recompute as shot temp and tint using new profile
            self.asShotTemp, self.asShotTint = multipliers2TemperatureAndTint(
                *1 / np.array(self.asShotMultipliers[:3]),
                self.XYZ2CameraMatrix, self.dngDict)
            # display updated as shot temp
            item = self.listWidget2.item(1)
            item.setText(item.text().split(":")[0] + ': %d' % self.asShotTemp)
            # invalidate cache
            self.layer.bufCache_HSV_CV32 = None
            self.dataChanged.emit(2)  # 2 = no postprocessing

        self.cameraProfilesCombo.currentIndexChanged.connect(
            cameraProfileUpdate)

        # denoising combo
        self.denoiseCombo = QComboBox()
        items = OrderedDict([('Off', 0), ('Medium', 1), ('Full', 2)])
        for key in items:
            self.denoiseCombo.addItem(key, items[key])

        # denoiseCombo index changed event handler
        def denoiseUpdate(value):
            self.denoiseValue = self.denoiseCombo.itemData(value)
            self.dataChanged.emit(1)

        self.denoiseCombo.currentIndexChanged.connect(denoiseUpdate)

        # overexposed area restoration
        self.overexpCombo = QComboBox()
        items = OrderedDict([('Clip', 0), ('Ignore', 1), ('Blend', 2),
                             ('Reconstruct', 3)])
        for key in items:
            self.overexpCombo.addItem(key, items[key])

        # overexpCombo index changed event handler
        def overexpUpdate(value):
            self.overexpValue = self.overexpCombo.itemData(value)
            self.dataChanged.emit(1)

        self.overexpCombo.currentIndexChanged.connect(overexpUpdate)

        # exp slider
        self.sliderExp = QbLUeSlider(Qt.Horizontal)
        self.sliderExp.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet)
        self.sliderExp.setRange(0, 100)

        self.sliderExp.setSingleStep(1)

        self.expLabel = QLabel()
        self.expLabel.setText("Exp.")

        self.expValue = QLabel()
        font = self.expValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("+1.0")
        h = metrics.height()
        self.expValue.setMinimumSize(w, h)
        self.expValue.setMaximumSize(w, h)
        self.expValue.setText(
            str("{:.1f}".format(self.slider2Exp(self.sliderExp.value()))))

        # exp done event handler
        def expUpdate(value):
            self.expValue.setText(
                str("{:+.1f}".format(
                    self.sliderExp2User(self.sliderExp.value()))))
            # move not yet terminated or value not modified
            if self.sliderExp.isSliderDown() or self.slider2Exp(
                    value) == self.expCorrection:
                return
            self.sliderExp.valueChanged.disconnect()
            self.sliderExp.sliderReleased.disconnect()
            # rawpy: expCorrection range is -2.0...3.0, boiling down to exp_shift range 2**(-2)=0.25...2**3=8.0
            self.expCorrection = self.slider2Exp(self.sliderExp.value())
            self.dataChanged.emit(1)
            self.sliderExp.valueChanged.connect(
                expUpdate)  # send new value as parameter
            self.sliderExp.sliderReleased.connect(lambda: expUpdate(
                self.sliderExp.value()))  # signal has no parameter

        self.sliderExp.valueChanged.connect(
            expUpdate)  # send new value as parameter
        self.sliderExp.sliderReleased.connect(lambda: expUpdate(
            self.sliderExp.value()))  # signal has no parameter

        # brightness slider
        brSlider = QbLUeSlider(Qt.Horizontal)
        brSlider.setRange(1, 101)

        self.sliderExp.setSingleStep(1)

        brSlider.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet)

        self.sliderBrightness = brSlider
        brLabel = QLabel()
        brLabel.setText("Bright.")

        self.brValue = QLabel()
        font = self.expValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("+99")
        h = metrics.height()
        self.brValue.setMinimumSize(w, h)
        self.brValue.setMaximumSize(w, h)
        self.brValue.setText(
            str("{:+d}".format(
                int(self.brSlider2User(self.sliderBrightness.value())))))

        # brightness done event handler
        def brUpdate(value):
            self.brValue.setText(
                str("{:+d}".format(
                    int(self.brSlider2User(self.sliderBrightness.value())))))
            # move not yet terminated or value not modified
            if self.sliderBrightness.isSliderDown() or self.slider2Br(
                    value) == self.brCorrection:
                return
            self.sliderBrightness.valueChanged.disconnect()
            self.sliderBrightness.sliderReleased.disconnect()
            self.brCorrection = self.slider2Br(self.sliderBrightness.value())
            self.dataChanged.emit(1)
            self.sliderBrightness.sliderReleased.connect(
                lambda: brUpdate(self.sliderBrightness.value()))
            self.sliderBrightness.valueChanged.connect(
                brUpdate)  # send new value as parameter

        self.sliderBrightness.valueChanged.connect(
            brUpdate)  # send new value as parameter
        self.sliderBrightness.sliderReleased.connect(
            lambda: brUpdate(self.sliderBrightness.value()))

        # contrast slider
        self.sliderCont = QbLUeSlider(Qt.Horizontal)
        self.sliderCont.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultBWStylesheet)
        self.sliderCont.setRange(0, 20)

        self.sliderCont.setSingleStep(1)

        self.contLabel = QLabel()
        self.contLabel.setText("Cont.")

        self.contValue = QLabel()
        font = self.contValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("100")
        h = metrics.height()
        self.contValue.setMinimumSize(w, h)
        self.contValue.setMaximumSize(w, h)
        self.contValue.setText(
            str("{:.0f}".format(self.slider2Cont(self.sliderCont.value()))))

        # cont done event handler
        def contUpdate(value):
            self.contValue.setText(
                str("{:.0f}".format(self.slider2Cont(
                    self.sliderCont.value()))))
            # move not yet terminated or value not modified
            if self.sliderCont.isSliderDown() or self.slider2Cont(
                    value) == self.tempCorrection:
                return
            self.sliderCont.valueChanged.disconnect()
            self.sliderCont.sliderReleased.disconnect()
            self.contCorrection = self.slider2Cont(self.sliderCont.value())
            self.contValue.setText(str("{:+d}".format(self.contCorrection)))
            # force to recalculate the spline
            self.layer.autoSpline = True
            self.dataChanged.emit(
                3)  # no postprocessing and no camera profile stuff
            self.sliderCont.valueChanged.connect(
                contUpdate)  # send new value as parameter
            self.sliderCont.sliderReleased.connect(lambda: contUpdate(
                self.sliderCont.value()))  # signal has no parameter

        self.sliderCont.valueChanged.connect(
            contUpdate)  # send new value as parameter
        self.sliderCont.sliderReleased.connect(lambda: contUpdate(
            self.sliderCont.value()))  # signal has no parameter

        # saturation slider
        self.sliderSat = QbLUeSlider(Qt.Horizontal)
        self.sliderSat.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultColorStylesheet)
        self.sliderSat.setRange(0, 100)

        self.sliderSat.setSingleStep(1)

        satLabel = QLabel()
        satLabel.setText("Sat.")

        self.satValue = QLabel()
        font = self.satValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("+10")
        h = metrics.height()
        self.satValue.setMinimumSize(w, h)
        self.satValue.setMaximumSize(w, h)
        self.satValue.setText(
            str("{:+d}".format(self.slider2Sat(self.sliderSat.value()))))
        """sat done event handler"""

        def satUpdate(value):
            self.satValue.setText(
                str("{:+d}".format(self.slider2Sat(self.sliderSat.value()))))
            # move not yet terminated or value not modified
            if self.sliderSat.isSliderDown() or self.slider2Sat(
                    value) == self.satCorrection:
                return
            self.sliderSat.valueChanged.disconnect()
            self.sliderSat.sliderReleased.disconnect()
            self.satCorrection = self.slider2Sat(self.sliderSat.value())
            self.dataChanged.emit(
                3)  # no post processing and no camera profile stuff
            self.sliderSat.valueChanged.connect(
                satUpdate)  # send new value as parameter
            self.sliderSat.sliderReleased.connect(lambda: satUpdate(
                self.sliderSat.value()))  # signal has no parameter

        self.sliderSat.valueChanged.connect(
            satUpdate)  # send new value as parameter
        self.sliderSat.sliderReleased.connect(lambda: satUpdate(
            self.sliderSat.value()))  # signal has no parameter

        # self.dataChanged.connect(self.updateLayer) # TODO 30/10/18 moved to base class
        self.setStyleSheet("QListWidget, QLabel {font : 7pt;}")

        # layout
        l = QVBoxLayout()
        l.addWidget(self.listWidget3)
        hl01 = QHBoxLayout()
        hl01.addWidget(QLabel('Camera Profile'))
        hl01.addWidget(self.cameraProfilesCombo)
        l.addLayout(hl01)
        hl0 = QHBoxLayout()
        hl0.addWidget(QLabel('Denoising'))
        hl0.addWidget(self.denoiseCombo)
        l.addLayout(hl0)
        hl00 = QHBoxLayout()
        hl00.addWidget(QLabel('Overexp. Restoration'))
        hl00.addWidget(self.overexpCombo)
        l.addLayout(hl00)
        hl1 = QHBoxLayout()
        hl1.addWidget(self.expLabel)
        hl1.addWidget(self.expValue)
        hl1.addWidget(self.sliderExp)
        l.addLayout(hl1)
        hl8 = QHBoxLayout()
        hl8.addWidget(brLabel)
        hl8.addWidget(self.brValue)
        hl8.addWidget(self.sliderBrightness)
        l.addLayout(hl8)
        l.addWidget(self.listWidget1)
        self.listWidget2.setStyleSheet(
            "QListWidget {border: 0px;} QListWidget::item {border: 0px; padding-left: 20px;}"
        )
        vl1 = QVBoxLayout()
        vl1.addWidget(QLabel('White Balance'))
        vl1.addWidget(self.listWidget2)
        gb1 = QGroupBox()
        gb1.setStyleSheet(
            "QGroupBox {border: 1px solid gray; border-radius: 4px}")
        hl2 = QHBoxLayout()
        hl2.addWidget(self.tempLabel)
        hl2.addWidget(self.tempValue)
        hl2.addWidget(self.sliderTemp)
        hl3 = QHBoxLayout()
        hl3.addWidget(self.tintLabel)
        hl3.addWidget(self.tintValue)
        hl3.addWidget(self.sliderTint)
        vl1.addLayout(hl2)
        vl1.addLayout(hl3)
        gb1.setLayout(vl1)
        l.addWidget(gb1)
        hl4 = QHBoxLayout()
        hl4.addWidget(self.contLabel)
        hl4.addWidget(self.contValue)
        hl4.addWidget(self.sliderCont)
        hl7 = QHBoxLayout()
        hl7.addWidget(satLabel)
        hl7.addWidget(self.satValue)
        hl7.addWidget(self.sliderSat)

        # separator
        sep = QFrame()
        sep.setFrameShape(QFrame.HLine)
        sep.setFrameShadow(QFrame.Sunken)
        l.addWidget(sep)
        l.addLayout(hl4)
        l.addLayout(hl7)
        #l.addLayout(hl5)
        l.addStretch(1)
        self.setLayout(l)
        self.adjustSize()
        self.setDefaults()
        #self.lookTable = dngProfileLookTable(self.dngDict)
        self.setWhatsThis("""<b>Development of raw files</b><br>
<b>Default settings</b> are a good starting point.<br>
A <b>Tone Curve</b> is applied to the raw image prior to postprocessing.<br> Il can be edited by checking the option
<b>Show Tone Curve</b>; this option works best with manual exposure.<br>
<b>Contrast</b> correction is based on an automatic algorithm well suited to multi-mode histograms.<br>
<b>Brightness, Contrast</b> and <b>Saturation</b> levels</b> are adjustable with the correponding sliders.<br>
The <b>Contrast Curve</b> can be edited manually by checking the option <b>Show Contrast Curve</b>.<br>
Uncheck <b>Auto Expose</b> to adjust the exposure manually.<br>
The <b>OverExp. Rest.</b> slider controls the mode of restoration of overexposed areas. 
Valid values are 0 to 3 (0=clip;1=unclip;2=blend;3=rebuild); (with Auto Exposed checked the mode is clip).<br>
<b> 
""")  # end of setWhatsThis
예제 #15
0
    def __init__(self, *args, **kwargs):
        ############################################################
        # Signals
        # Making QLayer inherit from QObject leads to
        # a buggy behavior of hasattr and getattr.
        # So, we don't add signals as first level class attributes.
        # Instead, we use instances of ad hoc signal containers (cf. utils.py)

        self.visibilityChanged = baseSignal_bool()
        self.colorPicked = baseSignal_Int2()

        ###########################################################
        # when a geometric transformation is applied to the whole image
        # each layer must be replaced with a transformed layer, recorded in tLayer
        # and tLayer.parentLayer keeps a reference to the original layer.
        self.tLayer = self
        self.parentLayer = self
        self.modified = False
        self.name = 'noname'
        self.visible = True
        # add signal instance (layer visibility change,..)
        #self.signals = baseSignals()
        self.isClipping = False
        self.role = kwargs.pop('role', '')
        self.tool = None
        # back link to parent image
        parentImage = kwargs.pop('parentImage', None)
        self.parentImage = weakProxy(parentImage)
        """
        if type(parentImage) in weakref.ProxyTypes:  # TODO 21/11/18 added weakref for back link
            self.parentImage = parentImage
        else:
            self.parentImage = weakref.proxy(parentImage)
        """
        #self.parentImage = kwargs.pop('parentImage', None)
        # layer opacity is used by QPainter operations.
        # Its value must be in the range 0.0...1.0
        self.opacity = 1.0
        self.compositionMode = QPainter.CompositionMode_SourceOver
        # The next two attributes are used by adjustment layers only.
        self.execute = lambda l=None, pool=None: l.updatePixmap(
        ) if l is not None else None
        self.options = {}
        # actionName is used by methods graphics***.writeToStream()
        self.actionName = 'actionNull'
        # view is the dock widget containing
        # the graphics form associated with the layer
        self.view = None
        # containers are initialized (only once) by
        # getCurrentMaskedImage. Their type is QLayer
        self.maskedImageContainer = None
        self.maskedThumbContainer = None
        # consecutive layers can be grouped.
        # A group is a list of QLayer objects
        self.group = []
        # layer offsets
        self.xOffset, self.yOffset = 0, 0
        self.Zoom_coeff = 1.0
        # clone dup layer shift and zoom  relative to current layer
        self.xAltOffset, self.yAltOffset = 0, 0
        self.AltZoom_coeff = 1.0
        super().__init__(*args, **kwargs)
        self.updatePixmap()
예제 #16
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None):
        super().__init__(parent=parent)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # back links to image
        self.targetImage = weakProxy(targetImage)
        self.img = weakProxy(targetImage)
        self.layer = weakProxy(layer)
        self.mainForm = mainForm
        # options
        options_dict = {'Normal Clone':cv2.NORMAL_CLONE, 'Mixed Clone':cv2.MIXED_CLONE, 'Monochrome Transfer':cv2.MONOCHROME_TRANSFER}
        options = list(options_dict.keys())
        self.layer.cloningMethod = options_dict['Normal Clone']
        self.layer.maskIsEnabled = True
        self.layer.maskIsSelected = True
        # mask all pixels, use a semi transparent mask
        self.layer.resetMask(maskAll=True, alpha=128)
        self.layer.autoclone = False
        self.layer.cloningMethod = cv2.NORMAL_CLONE
        self.options={}
        for op in options:
            self.options[op] = False
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        sel = options[0]
        self.listWidget1.select(self.listWidget1.items[sel])
        self.options[sel] = True
        # select event handler
        def onSelect1(item):
            for key in self.options:
                self.options[key] = item is self.listWidget1.items[key]
                if self.options[key]:
                    self.layer.cloningMethod = options_dict[key]
        self.listWidget1.onSelect = onSelect1
        # set initial selection to normal cloning
        item = self.listWidget1.items[options[0]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)
        pushButton1 = QPushButton('Clone')
        # button clicked event handler
        def f():
            layer = self.layer
            if vImage.isAllMasked(layer.mask):
                dlgWarn('Nothing to clone: unmask some pixels')
                return
            if layer.xAltOffset == 0.0 and layer.yAltOffset == 0.0:
                dlgWarn('Nothing to clone: Ctr+Alt+Drag the image ')
                return
            layer.applyCloning(seamless=True)
            layer.parentImage.onImageChanged()
            #layer.maskIsEnabled = False # done in onselect2
            #layer.maskIsSelected = False
        pushButton1.clicked.connect(f)
        pushButton2 = QPushButton('Reset')
        def g():
            layer = self.layer
            # mask all pixels
            layer.resetMask(maskAll=True)
            layer.maskIsSelected = True
            # reset clone layer
            layer.xAltOffset, layer.yAltOffset = 0.0, 0.0
            layer.AltZoom_coeff = 1.0
            layer.applyCloning(seamless=False)
            layer.parentImage.onImageChanged()
        pushButton2.clicked.connect(g)
        # layout
        layout = QVBoxLayout()
        layout.setContentsMargins(20, 0, 20, 25)  # left, top, right, bottom
        self.setLayout(layout)
        layout.addWidget(self.listWidget1)
        hl = QHBoxLayout()
        hl.addWidget(pushButton1)
        hl.addWidget(pushButton2)
        layout.addLayout(hl)
        self.setWhatsThis(
"""
<b>Cloning</b> :
Seamless replacement of a region of the image by another region from the same image (e.g. to erase an object):<br>
   &nbsp; 1) Select the Unmask/FG tool and paint the pixels to erase (use the Mask/BG tool to adjust if needed); <br>
   &nbsp; 2) Select the drag tool and while pressing <b>Ctrl-Alt</b>, drag or zoom the image shown in the painted region with the mouse; 
             the mask can be modified with the mask/unmask tools<br>
   &nbsp; 3) Click the Clone button to start the cloning.<br>
   &nbsp; 4) Redo steps 2 and 3 until the result is satisfactory.<br>
<b> While executing steps 1 to 4 above, make sure the that layer is the topmost visible layer</b>
"""
                        )
예제 #17
0
    def setLayers(self, mImg, delete=False):
        """
        Displays the layer stack of a mImage instance.
        @param mImg: image
        @type mImg: mImage
        @param delete:
        @type delete:
        """
        # close open adjustment windows
        # self.closeAdjustForms()
        self.clear(delete=delete)
        mImg.layerView = self
        # back link to image
        self.img = weakProxy(mImg)
        model = layerModel()
        model.setColumnCount(3)
        l = len(mImg.layersStack)

        # dataChanged event handler : enables edition of layer name
        def f(index1, index2):
            # index1 and index2 should be equal
            # only layer name should be editable
            # dropEvent emit dataChanged when setting item values. f must
            # return immediately from these calls, as they are possibly made with unconsistent data :
            # dragged rows are already removed from layersStack
            # and not yet removed from model.
            if l != self.model().rowCount():
                return
            # only name is editable
            if index1.column() != 1:
                return
            row = index1.row()
            stackIndex = l - row - 1
            mImg.layersStack[stackIndex].name = index1.data()

        model.dataChanged.connect(f)
        for r, lay in enumerate(reversed(mImg.layersStack)):
            try:
                lay.maskSettingsChanged.sig.disconnect()
            except RuntimeError:
                pass
            lay.maskSettingsChanged.sig.connect(self.updateRows)
            items = []
            # col 0 : visibility icon
            if lay.visible:
                item_visible = QStandardItem(
                    QIcon(":/images/resources/eye-icon.png"), "")
            else:
                item_visible = QStandardItem(
                    QIcon(":/images/resources/eye-icon-strike.png"), "")
            items.append(item_visible)
            # col 1 : image icon (for non-adjustment layer only) and name
            if len(lay.name) <= 30:
                name = lay.name
            else:
                name = lay.name[:28] + '...'
            if hasattr(lay, 'inputImg'):
                item_name = QStandardItem(name)
            else:
                # icon with very small dim causes QPainter error
                # QPixmap.fromImage bug ?
                smallImg = lay.resized(50, 50)
                w, h = smallImg.width(), smallImg.height()
                if w < h / 5 or h < w / 5:
                    item_name = QStandardItem(name)
                else:
                    item_name = QStandardItem(
                        QIcon(QPixmap.fromImage(smallImg)), name)
            # set tool tip to full name
            item_name.setToolTip(lay.name)
            items.append(item_name)
            item_mask = QStandardItem('m')
            items.append(item_mask)
            model.appendRow(items)
        self.setModel(model)
        self.horizontalHeader().hide()
        self.verticalHeader().hide()
        header = self.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
        header.setSectionResizeMode(2, QHeaderView.ResizeToContents)
        # lay out  the graphic forms into right pane
        forms = [
            item.view for item in mImg.layersStack
            if getattr(item, 'view', None) is not None
        ]
        for dock in forms:
            if TABBING:
                dockedForms = [item for item in forms if not item.isFloating()]
                dock.show()  # needed to get working tabbing
                if dock not in dockedForms and dock.tabbed:
                    if dockedForms:
                        window.tabifyDockWidget(dockedForms[-1], dock)
                    else:
                        window.addDockWidget(Qt.RightDockWidgetArea, dock)
            dock.setFloating(False)
        # select active layer
        self.selectRow(len(mImg.layersStack) - 1 - mImg.activeLayerIndex)
        activeLayer = mImg.getActiveLayer()
        layerview = activeLayer.view
        if layerview is not None:
            layerview.show()
            if TABBING:
                layerview.raise_()
            # lay out subcontrols of activeLayer
            form = layerview.widget()
            for dk in form.subControls:
                dk.setVisible(form.options[dk.widget().optionName])
                # clean up: we (re)dock all sucontrols
                dk.setFloating(False)  # emit topLevelChanged signal
        self.opacitySlider.setSliderPosition(int(activeLayer.opacity * 100))
        self.maskSlider.setSliderPosition(
            int(activeLayer.colorMaskOpacity * 100.0 / 255.0))
        ind = self.blendingModeCombo.findData(activeLayer.compositionMode)
        self.blendingModeCombo.setCurrentIndex(ind)
        self.previewOptionBox.setChecked(activeLayer.parentImage.useThumb)
        #activeLayer.maskColor
        self.updateForm()
        """                                                   # TODO removed 25/01/20 useless validate
예제 #18
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(parent=parent)
        self.setStyleSheet(
            'QRangeSlider * {border: 0px; padding: 0px; margin: 0px}')
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize + 100)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # link back to image layer
        self.layer = weakProxy(layer)
        """
        # using weak ref for back links
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        """
        # contrast spline viewer
        self.contrastForm = None
        # options
        optionList1, optionNames1 = ['Multi-Mode',
                                     'CLAHE'], ['Multi-Mode', 'CLAHE']
        self.listWidget1 = optionsWidget(
            options=optionList1,
            optionNames=optionNames1,
            exclusive=True,
            changed=lambda: self.dataChanged.emit())
        self.listWidget1.checkOption(self.listWidget1.intNames[0])
        self.listWidget1.setStyleSheet(
            "QListWidget {border: 0px;} QListWidget::item {border: 0px; padding-left: 0px;}"
        )
        optionList2, optionNames2 = ['High', 'manualCurve'], [
            'Preserve Highlights', 'Show Contrast Curve'
        ]

        def optionList2Change(item):
            if item.internalName == 'High':
                # force to recalculate the spline
                self.layer.autoSpline = True
            self.dataChanged.emit()

        self.listWidget2 = optionsWidget(options=optionList2,
                                         optionNames=optionNames2,
                                         exclusive=False,
                                         changed=optionList2Change)
        self.listWidget2.checkOption(self.listWidget2.intNames[0])
        self.listWidget2.setStyleSheet(
            "QListWidget {border: 0px;} QListWidget::item {border: 0px; padding-left: 0px;}"
        )
        self.options = UDict(
            (self.listWidget1.options, self.listWidget2.options))

        # contrast slider
        self.sliderContrast = QbLUeSlider(Qt.Horizontal)
        self.sliderContrast.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultIBWStylesheet)
        self.sliderContrast.setRange(0, 10)
        self.sliderContrast.setSingleStep(1)

        contrastLabel = QbLUeLabel()
        contrastLabel.setMaximumSize(150, 30)
        contrastLabel.setText("Contrast Level")
        contrastLabel.doubleClicked.connect(
            lambda: self.sliderContrast.setValue(
                self.contrast2Slider(self.contrastDefault)))

        self.contrastValue = QLabel()
        font = self.contrastValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("100")
        h = metrics.height()
        self.contrastValue.setMinimumSize(w, h)
        self.contrastValue.setMaximumSize(w, h)
        self.contrastValue.setText(
            str("{:d}".format(self.sliderContrast.value())))

        # contrast changed  event handler.
        def contrastUpdate(value):
            self.contrastValue.setText(
                str("{:d}".format(self.sliderContrast.value())))
            # move not yet terminated or value not modified
            if self.sliderContrast.isSliderDown() or self.slider2Contrast(
                    value) == self.contrastCorrection:
                return
            self.sliderContrast.valueChanged.disconnect()
            self.sliderContrast.sliderReleased.disconnect()
            self.contrastCorrection = self.slider2Contrast(
                self.sliderContrast.value())
            # force to recalculate the spline
            self.layer.autoSpline = True
            self.dataChanged.emit()
            self.sliderContrast.valueChanged.connect(contrastUpdate)
            self.sliderContrast.sliderReleased.connect(
                lambda: contrastUpdate(self.sliderContrast.value()))

        self.sliderContrast.valueChanged.connect(contrastUpdate)
        self.sliderContrast.sliderReleased.connect(
            lambda: contrastUpdate(self.sliderContrast.value()))

        # saturation slider
        self.sliderSaturation = QbLUeSlider(Qt.Horizontal)
        self.sliderSaturation.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultColorStylesheet)
        self.sliderSaturation.setRange(0, 100)
        self.sliderSaturation.setSingleStep(1)

        saturationLabel = QbLUeLabel()
        saturationLabel.setMaximumSize(150, 30)
        saturationLabel.setText("Saturation")
        saturationLabel.doubleClicked.connect(
            lambda: self.sliderSaturation.setValue(
                self.saturation2Slider(self.saturationDefault)))

        self.saturationValue = QLabel()
        font = self.saturationValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("100")
        h = metrics.height()
        self.saturationValue.setMinimumSize(w, h)
        self.saturationValue.setMaximumSize(w, h)
        self.saturationValue.setText(
            str("{:+d}".format(self.sliderContrast.value())))

        # saturation changed  event handler
        def saturationUpdate(value):
            self.saturationValue.setText(
                str("{:+d}".format(
                    int(
                        self.slidersaturation2User(
                            self.sliderSaturation.value())))))
            # move not yet terminated or value not modified
            if self.sliderSaturation.isSliderDown() or self.slider2Saturation(
                    value) == self.satCorrection:
                return
            self.sliderSaturation.valueChanged.disconnect()
            self.sliderSaturation.sliderReleased.disconnect()
            self.satCorrection = self.slider2Saturation(
                self.sliderSaturation.value())
            self.dataChanged.emit()
            self.sliderSaturation.valueChanged.connect(saturationUpdate)
            self.sliderSaturation.sliderReleased.connect(
                lambda: saturationUpdate(self.sliderSaturation.value()))

        self.sliderSaturation.valueChanged.connect(saturationUpdate)
        self.sliderSaturation.sliderReleased.connect(
            lambda: saturationUpdate(self.sliderSaturation.value()))

        # brightness slider
        self.sliderBrightness = QbLUeSlider(Qt.Horizontal)
        self.sliderBrightness.setStyleSheet(
            QbLUeSlider.bLueSliderDefaultBWStylesheet)
        self.sliderBrightness.setRange(0, 100)
        self.sliderBrightness.setSingleStep(1)

        brightnessLabel = QbLUeLabel()
        brightnessLabel.setMaximumSize(150, 30)
        brightnessLabel.setText("Brightness")
        brightnessLabel.doubleClicked.connect(
            lambda: self.sliderBrightness.setValue(
                self.brightness2Slider(self.brightnessDefault)))

        self.brightnessValue = QLabel()
        font = self.brightnessValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("100")
        h = metrics.height()
        self.brightnessValue.setMinimumSize(w, h)
        self.brightnessValue.setMaximumSize(w, h)
        self.brightnessValue.setText(
            str("{:+d}".format(self.sliderContrast.value())))

        # brightness changed  event handler
        def brightnessUpdate(value):
            self.brightnessValue.setText(
                str("{:+d}".format(
                    int(
                        self.sliderBrightness2User(
                            self.sliderBrightness.value())))))
            # move not yet terminated or value not modified
            if self.sliderBrightness.isSliderDown() or self.slider2Brightness(
                    value) == self.brightnessCorrection:
                return
            self.sliderBrightness.valueChanged.disconnect()
            self.sliderBrightness.sliderReleased.disconnect()
            self.brightnessCorrection = self.slider2Brightness(
                self.sliderBrightness.value())
            self.dataChanged.emit()
            self.sliderBrightness.valueChanged.connect(brightnessUpdate)
            self.sliderBrightness.sliderReleased.connect(
                lambda: brightnessUpdate(self.sliderBrightness.value()))

        self.sliderBrightness.valueChanged.connect(brightnessUpdate)
        self.sliderBrightness.sliderReleased.connect(
            lambda: brightnessUpdate(self.sliderBrightness.value()))

        # attributes initialized in setDefaults, declared here
        # for the sake of correctness
        self.contrastCorrection = None  # range
        self.satCorrection = None  # range -0.5..0.5
        self.brightnessCorrection = None  # range -0.5..0.5

        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignTop)
        gb1 = QGroupBox()
        gb1.setStyleSheet(
            "QGroupBox {border: 1px solid gray; border-radius: 4px}")
        l1 = QVBoxLayout()
        ct = QLabel()
        ct.setText('Contrast')
        l1.addWidget(ct)
        l1.addWidget(self.listWidget1)
        gb1.setLayout(l1)
        l.addWidget(gb1)
        l.addWidget(self.listWidget2)
        l.addWidget(contrastLabel)
        hl = QHBoxLayout()
        hl.addWidget(self.contrastValue)
        hl.addWidget(self.sliderContrast)
        l.addLayout(hl)
        l.addWidget(brightnessLabel)
        hl3 = QHBoxLayout()
        hl3.addWidget(self.brightnessValue)
        hl3.addWidget(self.sliderBrightness)
        l.addLayout(hl3)
        l.addWidget(saturationLabel)
        hl2 = QHBoxLayout()
        hl2.addWidget(self.saturationValue)
        hl2.addWidget(self.sliderSaturation)
        l.addLayout(hl2)
        self.setLayout(l)
        self.adjustSize()
        self.setStyleSheet("QListWidget, QLabel {font : 7pt;}")
        self.setDefaults()
        self.setWhatsThis("""<b>Contrast Brightness Saturation</b><br>
<b>Contrast</b> is enhanced using one of these two methods:<br>
  - <b>CLAHE</b> : increases the local contrast.<br>
  - <b>Multi-Mode</b> : increases the local contrast and the contrast between regions of the image.<br>
For both methods the contrast slider controls the level of the correction.<br>
With Multi-Mode enabled, use the option <b>Show Contrast Curve</b> to edit the correction curve and check
<b>Preserve Highlights</b> for softer highlights.<br>
Sliders are <b>reset</b> to their default value by double clicking the name of the slider.<br>
""")  # end setWhatsThis