Beispiel #1
0
    def __init__(self, targetImage=None, size=200, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
        self.setMinimumSize(size, 100)
        self.Label_Hist = QLabel()
        self.Label_Hist.setScaledContents(True)
        self.Label_Hist.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Preferred)
        self.setStyleSheet("QListWidget{border: 0px; font-size: 12px}")

        # options
        options1, optionNames1 = ['Original Image'], ['Source Image']
        self.listWidget1 = optionsWidget(options=options1,
                                         optionNames=optionNames1,
                                         exclusive=False)
        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.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.setContentsMargins(0, 0, 0, 2)
        h.addWidget(self.listWidget1)
        h.addWidget(self.listWidget2)
        vl = QVBoxLayout()
        vl.setAlignment(Qt.AlignTop)
        vl.addWidget(self.Label_Hist)
        vl.addLayout(h)
        vl.setContentsMargins(0, 0, 0, 2)  # left, top, right, bottom
        self.setLayout(vl)
Beispiel #2
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)

        # options
        optionList, optionNames = ['Auto'], ['Auto Orange Mask Removing']
        self.listWidget1 = optionsWidget(
            options=optionList,
            optionNames=optionNames,
            exclusive=False,
            changed=lambda: self.dataChanged.emit())
        self.listWidget1.checkOption(self.listWidget1.intNames[0])
        self.options = self.listWidget1.options

        # layout
        vl = QVBoxLayout()
        vl.addWidget(self.listWidget1)
        self.setLayout(vl)

        self.setDefaults()
        self.setWhatsThis("""
            <b> Negative Inversion</b><br>
            Negative films show an orange mask that must be corrected.<br>
            Automatic correction try to sample the mask color
            from an unexposed area of the negative film.<br>
            To do a <b>manual correction</b>, uncheck 'Auto Orange Mask Removing' and, next,
            Ctrl+Click the dark border of the image or, otherwise, a dark gray area.<br>
            """)  # end of setWhatsThis
Beispiel #3
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        # options
        optionList1, optionNames1 = ['Free', 'Rotation', 'Translation'], [
            'Free Transformation', 'Rotation', 'Translation'
        ]
        self.listWidget1 = optionsWidget(options=optionList1,
                                         optionNames=optionNames1,
                                         exclusive=True,
                                         changed=self.dataChanged)
        optionList2, optionNames2 = ['Transparent'
                                     ], ['Set Transparent Pixels To Black']
        self.listWidget2 = optionsWidget(options=optionList2,
                                         optionNames=optionNames2,
                                         exclusive=False,
                                         changed=self.dataChanged)
        self.options = UDict(
            (self.listWidget1.options, self.listWidget2.options))
        # set initial selection to Perspective
        self.listWidget1.checkOption(optionList1[0])

        pushButton1 = QPushButton(' Reset Transformation ')
        pushButton1.adjustSize()

        pushButton1.clicked.connect(self.reset)

        # 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.setDefaults()
        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.
                        """)  # end of setWhatsThis
    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()
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 mainForm=None):
        super().__init__(targetImage=targetImage,
                         axeSize=axeSize,
                         layer=layer,
                         parent=parent,
                         mainForm=mainForm)
        # Brightness curve
        cubic = activeCubicSpline(axeSize)
        graphicsScene = self.scene()
        graphicsScene.addItem(cubic)
        graphicsScene.cubicRGB = cubic
        cubic.channel = channelValues.RGB
        cubic.histImg = self.scene().layer.inputImg().histogram(
            size=graphicsScene.axeSize,
            bgColor=graphicsScene.bgColor,
            chans=[],
            mode='Luminosity')
        cubic.initFixedPoints()
        # Red curve
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicR = cubic
        cubic.channel = channelValues.Red
        cubic.histImg = self.scene().layer.inputImg().histogram(
            size=graphicsScene.axeSize,
            bgColor=graphicsScene.bgColor,
            chans=channelValues.Red)
        cubic.initFixedPoints()
        # Green curve
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicG = cubic
        cubic.channel = channelValues.Green
        cubic.histImg = self.scene().layer.inputImg().histogram(
            size=graphicsScene.axeSize,
            bgColor=graphicsScene.bgColor,
            chans=channelValues.Green)
        cubic.initFixedPoints()
        # Blue curve
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicB = cubic
        cubic.channel = channelValues.Blue
        cubic.histImg = self.scene().layer.inputImg().histogram(
            size=graphicsScene.axeSize,
            bgColor=graphicsScene.bgColor,
            chans=channelValues.Blue)
        cubic.initFixedPoints()
        # set current curve to brightness
        graphicsScene.cubicItem = graphicsScene.cubicRGB
        graphicsScene.cubicItem.setVisible(True)

        # buttons
        pushButton1 = QbLUePushButton("Reset Current")
        pushButton1.move(100, 20)
        pushButton1.adjustSize()
        pushButton1.clicked.connect(self.resetCurve)
        graphicsScene.addWidget(pushButton1)
        pushButton2 = QbLUePushButton("Reset R,G,B")
        pushButton2.move(100, 50)
        pushButton2.adjustSize()
        pushButton2.clicked.connect(self.resetAllCurves)
        graphicsScene.addWidget(pushButton2)

        # options
        options = ['RGB', 'Red', 'Green', 'Blue']
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        self.listWidget1.setGeometry(
            0, 10,
            self.listWidget1.sizeHintForColumn(0) + 5,
            self.listWidget1.sizeHintForRow(0) * len(options) + 5)
        graphicsScene.addWidget(self.listWidget1)
        # selection changed handler
        curves = [
            graphicsScene.cubicRGB, graphicsScene.cubicR, graphicsScene.cubicG,
            graphicsScene.cubicB
        ]
        curveDict = dict(zip(options, curves))

        def onSelect1(item):
            self.scene().cubicItem.setVisible(False)
            self.scene().cubicItem = curveDict[item.text()]
            pushButton2.setEnabled(item.text() != 'RGB')
            self.scene().cubicItem.setVisible(True)
            l = self.scene().layer
            l.applyToStack()
            l.parentImage.onImageChanged()
            # Force redraw histogram
            self.scene().invalidate(
                QRectF(0.0, -self.scene().axeSize,
                       self.scene().axeSize,
                       self.scene().axeSize), QGraphicsScene.BackgroundLayer)

        self.listWidget1.onSelect = onSelect1
        # set initial selection to RGB
        item = self.listWidget1.items[options[0]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)
        self.setWhatsThis("""<b>RGB curves</b><br>""" + self.whatsThis())

        def f():
            l = self.scene().layer
            l.applyToStack()
            l.parentImage.onImageChanged()

        self.scene().cubicRGB.curveChanged.sig.connect(f)
        self.scene().cubicR.curveChanged.sig.connect(f)
        self.scene().cubicG.curveChanged.sig.connect(f)
        self.scene().cubicB.curveChanged.sig.connect(f)
Beispiel #6
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.setWindowTitle('grabcut')
        button0 = QPushButton('Segment')

        # button slot
        def f():
            self.layer.noSegment = False
            self.layer.applyToStack()
            window.label.img.onImageChanged()
            # manual segmentation only
            self.layer.noSegment = True

        button0.clicked.connect(f)

        button1 = QPushButton('Reset')
        button1.clicked.connect(self.reset)

        self.spBox = QSpinBox()
        self.spBox.setRange(1, 10)

        # spBox slot
        def f2(iterCount):
            self.nbIter = self.spBox.value()
            self.dataChanged.emit()

        self.spBox.valueChanged.connect(f2)
        spBoxLabel = QLabel()
        spBoxLabel.setText('Iterations')

        self.spBox1 = QSpinBox()
        self.spBox1.setRange(0, 20)
        spBox1Label = QLabel()
        spBox1Label.setText('Contour Redo Radius')

        # spBox1 slot
        def f1(margin):
            self.contourMargin = self.spBox1.value()
            self.dataChanged.emit()

        self.spBox1.valueChanged.connect(f1)

        # options
        optionList1, optionNames1 = ['Clipping Layer'], ['Clipping Layer']
        self.listWidget1 = optionsWidget(
            options=optionList1,
            optionNames=optionNames1,
            exclusive=False,
            changed=lambda: self.dataChanged.emit())
        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

        # 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(button0)
        h3.addWidget(button1)
        vLay.addLayout(h3)
        self.setLayout(vLay)

        self.setDefaults()
        self.setWhatsThis(""" <b>Segmentation (Object extraction)</b><br>  
              Select the object to extract with the rectangle Marquee Tool. Next, press the <i>Segment</i> button.<br>
              The background of the segmented image is transparent : to <b>mask the underlying layers</b> check the
              option <i>Clipping Layer.</i><br>
              To <b>fix the selection</b>, paint eventual misclassed pixels with the foreground (FG) or background (BG) tools.<br>
              To <b>redo the segmentation of a region</b> (e.g. a border area) hold down the Ctrl key while painting the area
              and press again <i>segment.</i><br>
              To <b>redo the segmentation of the whole contour</b> set <i>Contour Redo Radius</i> to a value >= 1 and
              press <i>Segment.</i><br>
              To <b>smooth the contour</b> right click the layer row in the <i>Layers</i> panel
              and choose <i>Smooth Mask</i> from the context menu.<br>
            """)  # end setWhatsThis
    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
Beispiel #8
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        # connect layer selectionChanged signal
        self.layer.selectionChanged.sig.connect(self.updateLayer)
        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))  # filters is not a dict: don't use UDict here

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

        # sliders
        self.sliderRadius = QbLUeSlider(Qt.Horizontal)
        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.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.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)

        # value change/done slot
        def formUpdate():
            self.radiusValue.setText(str('%d ' % self.sliderRadius.value()))
            self.amountValue.setText(str('%d ' % self.sliderAmount.value()))
            self.toneValue.setText(str('%d ' % self.sliderTone.value()))
            if self.sliderRadius.isSliderDown(
            ) or self.sliderAmount.isSliderDown(
            ) or self.sliderTone.isSliderDown():
                return
            try:
                for slider in [
                        self.sliderRadius, self.sliderAmount, self.sliderTone
                ]:
                    slider.valueChanged.disconnect()
                    slider.sliderReleased.disconnect()
            except RuntimeError:
                pass
            self.tone = self.sliderTone.value()
            self.radius = self.sliderRadius.value()
            self.amount = self.sliderAmount.value()
            self.dataChanged.emit()
            for slider in [
                    self.sliderRadius, self.sliderAmount, self.sliderTone
            ]:
                slider.valueChanged.connect(formUpdate)
                slider.sliderReleased.connect(formUpdate)

        for slider in [self.sliderRadius, self.sliderAmount, self.sliderTone]:
            slider.valueChanged.connect(formUpdate)
            slider.sliderReleased.connect(formUpdate)

        # layout
        l = QVBoxLayout()
        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)

        self.setDefaults()
        self.setWhatsThis("""
                       <b>Unsharp Mask</b> and <b>Sharpen Mask</b> are used to sharpen an image.
                       Unsharp Mask usually gives best results.<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,
                       but it may be slow.<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 (rectangle) tool.<br>
                       Ctrl Click <b>clears the selection</b><br>
                       
                    """)  # end setWhatsThis
Beispiel #9
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.setMinimumSize(axeSize, axeSize + 100)
        # 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')
        l.setAlignment(Qt.AlignTop)
        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>
            <b>Brightness</b> and <b>Saturation</b> corrections are non linear to limit clipping.<br>
            Sliders are <b>reset</b> to their default value by double clicking the name of the slider.<br>
            """)  # end setWhatsThis
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, mainForm=None):
        super().__init__(targetImage=targetImage, axeSize=axeSize, layer=layer, parent=parent, mainForm=mainForm)
        graphicsScene = self.scene()
        #########
        # L curve
        #########
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicR = cubic
        cubic.channel = channelValues.L
        # get histogram as a Qimage
        cubic.histImg = graphicsScene.layer.inputImg().histogram(size=graphicsScene.axeSize,
                                                                    bgColor=graphicsScene.bgColor, range=(0, 1),
                                                                    chans=channelValues.L, mode='Lab')
        # L curve use the default axes
        cubic.axes = graphicsScene.defaultAxes
        cubic.initFixedPoints()
        cubic.axes.setVisible(False)
        cubic.setVisible(False)
        ##########
        # a curve (Green--> Magenta axis)
        #########
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicG = cubic
        cubic.channel = channelValues.a
        cubic.histImg = graphicsScene.layer.inputImg().histogram(size=graphicsScene.axeSize,
                                                                    bgColor=graphicsScene.bgColor, range=(-100, 100),
                                                                    chans=channelValues.a, mode='Lab')
        #  add specific axes
        gradient = QRadialGradient()
        gradient.setCenter(QPoint(0, 1))
        gradient.setRadius(axeSize*1.4)
        gradient.setColorAt(0.0, Qt.green)
        gradient.setColorAt(1.0, Qt.magenta)
        cubic.axes = self.drawPlotGrid(axeSize, gradient)
        graphicsScene.addItem(cubic.axes)
        cubic.initFixedPoints()
        cubic.axes.setVisible(False)
        cubic.setVisible(False)

        # b curve (Blue-->Yellow axis)
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicB = cubic
        cubic.channel = channelValues.b
        cubic.histImg = graphicsScene.layer.inputImg().histogram(size=graphicsScene.axeSize,
                                                                    bgColor=graphicsScene.bgColor, range=(-100, 100),
                                                                    chans=channelValues.b, mode='Lab')
        # add specific axes
        gradient.setColorAt(0.0, Qt.blue)
        gradient.setColorAt(1.0, Qt.yellow)
        cubic.axes = self.drawPlotGrid(axeSize, gradient)
        graphicsScene.addItem(cubic.axes)
        cubic.initFixedPoints()
        cubic.axes.setVisible(False)
        cubic.setVisible(False)

        # set current to L curve and axes
        graphicsScene.cubicItem = graphicsScene.cubicR
        graphicsScene.cubicItem.setVisible(True)
        graphicsScene.cubicItem.axes.setVisible(True)
        # buttons
        pushButton1 = QPushButton("Reset Current")
        pushButton1.move(100, 20)
        pushButton1.adjustSize()
        pushButton1.clicked.connect(self.resetCurve)
        graphicsScene.addWidget(pushButton1)
        pushButton2 = QPushButton("Reset All")
        pushButton2.move(100, 50)
        pushButton2.adjustSize()
        pushButton2.clicked.connect(self.resetAllCurves)
        graphicsScene.addWidget(pushButton2)
        # options
        options = ['L', 'a', 'b']
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        self.listWidget1.setGeometry(0, 10, self.listWidget1.sizeHintForColumn(0) + 5, self.listWidget1.sizeHintForRow(0) * len(options) + 5)
        graphicsScene.addWidget(self.listWidget1)

        # selection changed handler
        curves = [graphicsScene.cubicR, graphicsScene.cubicG, graphicsScene.cubicB]
        curveDict = dict(zip(options, curves))
        def onSelect1(item):
            cubicItem = self.scene().cubicItem
            cubicItem.setVisible(False)
            cubicItem.axes.setVisible(False)
            self.scene().cubicItem = curveDict[item.text()]
            self.scene().cubicItem.setVisible(True)
            self.scene().cubicItem.axes.setVisible(True)
            # Force to redraw  histogram
            self.scene().invalidate(QRectF(0.0, -self.scene().axeSize, self.scene().axeSize, self.scene().axeSize),
                                    QGraphicsScene.BackgroundLayer)
        self.listWidget1.onSelect = onSelect1
        # set initial selection to L
        item = self.listWidget1.items[options[0]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)
        self.setWhatsThis("""<b>Lab curves</b><br>""" + self.whatsThis())
        def f():
            l = graphicsScene.layer
            l.applyToStack()
            l.parentImage.onImageChanged()
        self.scene().cubicR.curveChanged.sig.connect(f)
        self.scene().cubicG.curveChanged.sig.connect(f)
        self.scene().cubicB.curveChanged.sig.connect(f)
Beispiel #11
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None, colorModel='HSV'):
        super().__init__(targetImage=targetImage, axeSize=axeSize, layer=layer, parent=parent)
        graphicsScene = self.scene()
        graphicsScene.colorModel = colorModel

        # hue curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicR = cubic
        cubic.channel = channelValues.Hue

        cubic.initFixedPoints()

        # sat curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicG = cubic
        cubic.channel = channelValues.Sat

        cubic.initFixedPoints()

        # brightness curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicB = cubic
        cubic.channel = channelValues.Br

        cubic.initFixedPoints()

        # init histograms
        self.updateHists()

        # set current curve to sat
        graphicsScene.cubicItem = graphicsScene.cubicG
        graphicsScene.cubicItem.setVisible(True)

        # buttons
        pushButton1 = QPushButton("Reset Current")
        pushButton1.clicked.connect(self.resetCurve)
        pushButton2 = QPushButton("Reset All")
        pushButton2.clicked.connect(self.resetAllCurves)
        # options
        options = ['H', 'S', 'B']
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        self.listWidget1.setGeometry(0, 0, self.listWidget1.sizeHintForColumn(0) + 5,
                                     self.listWidget1.sizeHintForRow(0) * len(options) + 5)

        # selection changed handler
        curves = [graphicsScene.cubicR, graphicsScene.cubicG, graphicsScene.cubicB]
        curveDict = dict(zip(options, curves))

        def onSelect1(item):
            self.scene().cubicItem.setVisible(False)
            self.scene().cubicItem = curveDict[item.text()]
            self.scene().cubicItem.setVisible(True)
            # draw  histogram
            self.scene().invalidate(QRectF(0.0, -self.scene().axeSize, self.scene().axeSize,
                                           self.scene().axeSize), QGraphicsScene.BackgroundLayer)

        self.listWidget1.onSelect = onSelect1
        # set initial selection to Saturation
        item = self.listWidget1.items[options[1]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)

        # layout
        gl = QGridLayout()
        gl.addWidget(self.listWidget1, 0, 0, 2, 1)
        for i, button in enumerate([pushButton1, pushButton2]):
            gl.addWidget(button, i, 1)
        self.addCommandLayout(gl)

        self.setWhatsThis("""<b>HSV curves</b><br>""" + self.whatsThis())

        def f():
            layer = graphicsScene.layer
            layer.applyToStack()
            layer.parentImage.onImageChanged()
        self.scene().cubicR.curveChanged.sig.connect(f)
        self.scene().cubicG.curveChanged.sig.connect(f)
        self.scene().cubicB.curveChanged.sig.connect(f)
Beispiel #12
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.defaultFilterStart = 0
        self.defaultFilterEnd = 99
        self.filterStart = self.defaultFilterStart
        self.filterEnd = self.defaultFilterEnd

        self.kernelCategory = blendFilterIndex.GRADUALNONE  # TODO kernelCategory should be renamed as filterIndex 5/12/18
        # options
        optionList, optionNames = ['Gradual Top', 'Gradual Bottom'], ['Top To Bottom', 'Bottom To Top']
        filters = [blendFilterIndex.GRADUALTB, blendFilterIndex.GRADUALBT]  # , blendFilterIndex.GRADUALNONE]
        self.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
            try:
                self.sliderFilterRange.startValueChanged.disconnect()
                self.sliderFilterRange.endValueChanged.disconnect()
                self.sliderFilterRange.rangeDone.disconnect()
            except RuntimeError:
                pass
            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)

        # layout
        l = QVBoxLayout()
        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()
Beispiel #13
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.tempCorrection = 6500
        self.tintCorrection = 1.0
        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("Filter 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("00000")
        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)

        self.tintLabel = QbLUeLabel()
        self.tintLabel.setMaximumSize(150, 30)
        self.tintLabel.setText("Tint")
        self.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 slot
        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
            try:
                self.sliderTemp.valueChanged.disconnect()
                self.sliderTemp.sliderReleased.disconnect()
            except RuntimeError:
                pass
            self.tempCorrection = self.slider2Temp(value)
            self.dataChanged.emit()
            self.sliderTemp.valueChanged.connect(tempUpdate)
            self.sliderTemp.sliderReleased.connect(
                lambda: tempUpdate(self.sliderTemp.value()))

        # tint change slot
        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
            try:
                self.sliderTint.valueChanged.disconnect()
                self.sliderTint.sliderReleased.disconnect()
            except RuntimeError:
                pass
            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(self.tintLabel)
        hl1 = QHBoxLayout()
        hl1.addWidget(self.tintValue)
        hl1.addWidget(self.sliderTint)
        l.addLayout(hl1)
        self.setLayout(l)
        self.adjustSize()
        self.setDefaults()
        self.setWhatsThis("""<b>Color Temperature</b><br>
                        <b>Photo Filter</b> uses the multiply blending mode to mimic a warming or cooling filter
                        put in front of the camera lens. The luminosity of the resulting image is corrected.<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
Beispiel #14
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
Beispiel #15
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
    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
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 colorModel='HSV',
                 mainForm=None):
        super().__init__(targetImage=targetImage,
                         axeSize=axeSize,
                         layer=layer,
                         parent=parent,
                         mainForm=mainForm)
        graphicsScene = self.scene()
        graphicsScene.colorModel = colorModel

        # hue curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicR = cubic
        cubic.channel = channelValues.Hue
        """
        cubic.histImg = graphicsScene.layer.histogram(size=axeSize,
                                                       bgColor=graphicsScene.bgColor, range=(0, 360),
                                                       chans=channelValues.Hue, mode='HSpB')
        """
        cubic.initFixedPoints()

        # sat curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicG = cubic
        cubic.channel = channelValues.Sat
        """
        cubic.histImg = graphicsScene.layer.histogram(size=axeSize,
                                                      bgColor=graphicsScene.bgColor, range=(0,1),
                                                      chans=channelValues.Sat, mode='HSpB')
        """
        cubic.initFixedPoints()

        # brightness curve init.
        cubic = activeCubicSpline(axeSize)
        graphicsScene.addItem(cubic)
        graphicsScene.cubicB = cubic
        cubic.channel = channelValues.Br
        """
        cubic.histImg = graphicsScene.layer.histogram(size=axeSize,
                                                      bgColor=graphicsScene.bgColor,
                                                      range=(0,1), chans=channelValues.Br, mode='HSpB')
        """
        cubic.initFixedPoints()

        # init histograms
        self.updateHists()

        # set current curve to sat
        graphicsScene.cubicItem = graphicsScene.cubicG
        graphicsScene.cubicItem.setVisible(True)

        # buttons
        pushButton1 = QPushButton("Reset Current")
        pushButton1.move(100, 20)
        pushButton1.adjustSize()
        pushButton1.clicked.connect(self.resetCurve)
        graphicsScene.addWidget(pushButton1)
        pushButton2 = QPushButton("Reset All")
        pushButton2.move(100, 50)
        pushButton2.adjustSize()
        pushButton2.clicked.connect(self.resetAllCurves)
        graphicsScene.addWidget(pushButton2)
        # options
        options = ['H', 'S', 'B']
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        self.listWidget1.setGeometry(
            0, 10,
            self.listWidget1.sizeHintForColumn(0) + 5,
            self.listWidget1.sizeHintForRow(0) * len(options) + 5)
        graphicsScene.addWidget(self.listWidget1)

        # selection changed handler
        curves = [
            graphicsScene.cubicR, graphicsScene.cubicG, graphicsScene.cubicB
        ]
        curveDict = dict(zip(options, curves))

        def onSelect1(item):
            self.scene().cubicItem.setVisible(False)
            self.scene().cubicItem = curveDict[item.text()]
            self.scene().cubicItem.setVisible(True)
            # draw  histogram
            self.scene().invalidate(
                QRectF(0.0, -self.scene().axeSize,
                       self.scene().axeSize,
                       self.scene().axeSize), QGraphicsScene.BackgroundLayer)

        self.listWidget1.onSelect = onSelect1
        # set initial selection to Saturation
        item = self.listWidget1.items[options[1]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)
        self.setWhatsThis("""<b>HSV curves</b><br>""" + self.whatsThis())

        def f():
            layer = graphicsScene.layer
            layer.applyToStack()
            layer.parentImage.onImageChanged()

        self.scene().cubicR.curveChanged.sig.connect(f)
        self.scene().cubicG.curveChanged.sig.connect(f)
        self.scene().cubicB.curveChanged.sig.connect(f)
Beispiel #18
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
    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)
    def __init__(self, axeSize=500, layer=None, parent=None):
        super(noiseForm, self).__init__(parent=parent)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(axeSize, axeSize)
        self.setAttribute(Qt.WA_DeleteOnClose)
        # link back to image layer
        # using weak ref for back links
        if type(layer) in weakref.ProxyTypes:
            self.layer = layer
        else:
            self.layer = weakref.proxy(layer)
        # attribute initialized in setDefaults
        # defined here for the sake of correctness
        self.noiseCorrection = 0

        # options
        optionList = ['Wavelets', 'Bilateral', 'NLMeans']
        self.listWidget1 = optionsWidget(
            options=optionList,
            exclusive=True,
            changed=lambda: self.dataChanged.emit(True))
        self.listWidget1.checkOption(self.listWidget1.intNames[0])
        self.options = self.listWidget1.options

        # threshold slider
        self.sliderThr = QbLUeSlider(Qt.Horizontal)
        self.sliderThr.setStyleSheet(QbLUeSlider.bLueSliderDefaultBWStylesheet)
        self.sliderThr.setTickPosition(QSlider.TicksBelow)
        self.sliderThr.setRange(0, 10)
        self.sliderThr.setSingleStep(1)

        self.sliderThr.valueChanged.connect(self.thrUpdate)
        self.sliderThr.sliderReleased.connect(lambda: self.thrUpdate(
            self.sliderThr.value()))  # signal has no parameter)

        self.thrLabel = QLabel()
        self.thrLabel.setMaximumSize(150, 30)
        self.thrLabel.setText("level")

        self.thrValue = QLabel()
        font = self.thrValue.font()
        metrics = QFontMetrics(font)
        w = metrics.width("0000")
        h = metrics.height()
        self.thrValue.setMinimumSize(w, h)
        self.thrValue.setMaximumSize(w, h)
        self.thrValue.setText(
            str("{:.0f}".format(self.slider2Thr(self.sliderThr.value()))))
        # self.dataChanged.connect(self.updateLayer)
        # self.setStyleSheet("QListWidget, QLabel {font : 7pt;}")
        # layout
        l = QVBoxLayout()
        #l.setAlignment(Qt.AlignBottom)
        l.addWidget(self.listWidget1)
        hl1 = QHBoxLayout()
        hl1.addWidget(self.thrLabel)
        hl1.addWidget(self.thrValue)
        hl1.addWidget(self.sliderThr)
        l.addLayout(hl1)
        l.setContentsMargins(20, 0, 20, 25)  # left, top, right, bottom
        #l.setContentsMargins(10, 10, 10, 10)  # left, top, right, bottom
        self.setLayout(l)
        self.adjustSize()
        self.setDefaults()
        self.setWhatsThis("""<b>Noise Reduction</b><br>
   <b>Bilateral Filtering</b> is the fastest method.<br>
   <b>NLMeans</b> (Non Local Means) and <b>Wavelets</b> are slower,
   but they usually give better results.<br>
   It is possible to <b>limit the application of all methods 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 of setWhatsThis
Beispiel #21
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)

        #######################################
        # 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.listWidget2 = optionsWidget(
            options=optionList1,
            optionNames=optionNames1,
            exclusive=True,
            changed=lambda: self.dataChanged.emit(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)  # signal send new value as parameter
        self.sliderTemp.sliderReleased.connect(lambda: self.tempUpdate(
            self.sliderTemp.value()))  # signal pass no parameter

        # tint slider
        self.sliderTint = QbLUeSlider(Qt.Horizontal)
        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 pass 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
            try:
                self.sliderExp.valueChanged.disconnect()
                self.sliderExp.sliderReleased.disconnect()
            except RuntimeError:
                pass
            # 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 pass no parameter

        self.sliderExp.valueChanged.connect(
            expUpdate)  # send new value as parameter
        self.sliderExp.sliderReleased.connect(lambda: expUpdate(
            self.sliderExp.value()))  # signal pass 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
            try:
                self.sliderBrightness.valueChanged.disconnect()
                self.sliderBrightness.sliderReleased.disconnect()
            except RuntimeError:
                pass
            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
            try:
                self.sliderCont.valueChanged.disconnect()
                self.sliderCont.sliderReleased.disconnect()
            except RuntimeError:
                pass
            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
            try:
                self.sliderSat.valueChanged.disconnect()
                self.sliderSat.sliderReleased.disconnect()
            except RuntimeError:
                pass
            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.addStretch(1)
        self.setLayout(l)
        self.adjustSize()
        self.setDefaults()
        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> 
                    The cuvre 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>
                    """)  # end of setWhatsThis
Beispiel #22
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, 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)
        # 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  # cloning is slow : no cloning is performed by applyToStack() calls
        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 slot

        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')

        # Clone button clicked slot
        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()

        pushButton1.clicked.connect(f)
        pushButton2 = QPushButton('Reset')

        # reset button clicked slot
        def g():
            layer = self.layer
            # mask all pixels
            layer.resetMask(maskAll=True, alpha=128)
            layer.setMaskEnabled(color=True)
            # reset clone layer
            layer.xAltOffset, layer.yAltOffset = 0.0, 0.0
            layer.AltZoom_coeff = 1.0
            layer.applyCloning(seamless=False, showTranslated=True)
            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.setDefaults()

        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;<br>
                               &nbsp; 3) Click the Clone button to start the cloning.<br>
                            Redo steps 1 to 3 until the result is satisfactory. Eventually 
                            use <b>Mask Erode</b> from the layer context menu to smooth mask edges.<br>
                            <b> While executing steps 1 to 4 above, make sure that
                            the cloning layer is the topmost visible layer</b><br>
                            
                            """)  # end of setWhatsthis
    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.
""")