Пример #1
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
Пример #2
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', 'Align'
        ], ['Free Transformation', 'Rotation', 'Translation', 'Align']
        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.<br>
                          Check <i>Align</i> to align the image with the underlying layers (for example to blend bracketed images). 
                          Note that this option may modify or cancel the current transformation.
                        """)  # end of setWhatsThis
Пример #3
0
 def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
     super().__init__(parent=parent, targetImage=targetImage, layer=layer)
     self.setMinimumSize(axeSize, axeSize + 80)
     # color wheel size
     self.cwSize = axeSize * 0.95
     self.setAttribute(Qt.WA_DeleteOnClose)
     # options
     optionList = ['Monochrome']
     listWidget1 = optionsWidget(options=optionList, exclusive=False, changed=self.dataChanged)
     listWidget1.setMinimumWidth(listWidget1.sizeHintForColumn(0) + 5)
     listWidget1.setMinimumHeight(listWidget1.sizeHintForRow(0) * len(optionList) + 5)
     self.options = listWidget1.options
     # barycentric coordinate basis : the 3 base points form an equilateral triangle
     h = self.cwSize - 50
     s = h * 2 / np.sqrt(3)
     self.R, self.G, self.B = QPointF(10, h + 20), QPointF(10 + s, h + 20), QPointF(10 + s / 2, 20)
     # Conversion matrix from cartesian coordinates (x, y, 1) to barycentric coordinates (alpha, beta, gamma)
     self.M = np.array([[self.R.x(), self.G.x(), self.B.x()],
                        [self.R.y(), self.G.y(), self.B.y()],
                        [1,            1,           1      ]])
     self.invM = np.linalg.inv(self.M)
     self.setBackgroundImage()
     # active points
     self.rPoint = activeMixerPoint(self.R.x(), self.R.y(), color=Qt.red, fillColor=Qt.white)
     self.rPoint.source = self.R
     self.gPoint = activeMixerPoint(self.G.x(), self.G.y(), color=Qt.green, fillColor=Qt.white)
     self.gPoint.source = self.G
     self.bPoint = activeMixerPoint(self.B.x(), self.B.y(), color=Qt.blue, fillColor=Qt.white)
     self.bPoint.source = self.B
     graphicsScene = self.scene()
     for point in [self.rPoint, self.gPoint, self.bPoint]:
         graphicsScene.addItem(point)
     gl = QGridLayout()
     gl.addWidget(listWidget1, 0, 0, 2, 2)
     self.addCommandLayout(gl)
     self.setDefaults()
     self.setWhatsThis(
                     """<b>Channel Mixer</b><br>
                     To <b>mix the R, G, B channels</b>, drag the 3 control points inside the triangle.<br>
                     The triangle vertices and the control points correspond to channels.
                     The closer a control point is to a vertex, the greater the corresponding channel contribution.  
                     """
                     )  # end of setWhatsThis
Пример #4
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
Пример #5
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)
Пример #6
0
 def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
     super().__init__(parent=parent, targetImage=targetImage, layer=layer)
     self.setMinimumSize(axeSize, axeSize + 100)
     # color wheel size
     self.cwSize = axeSize * 0.95
     self.setAttribute(Qt.WA_DeleteOnClose)
     # options
     optionList = ['Monochrome', 'Luminosity']
     listWidget1 = optionsWidget(options=optionList,
                                 exclusive=False,
                                 changed=self.dataChanged,
                                 flow=optionsWidget.LeftToRight)
     listWidget1.setMaximumHeight(
         listWidget1.sizeHintForRow(0) +
         5)  # mandatory although sizePolicy is set to minimum !
     self.options = listWidget1.options
     # barycentric coordinate basis : the 3 base points form an equilateral triangle
     h = self.cwSize - 50
     s = h * 2 / np.sqrt(3)
     self.R, self.G, self.B = QPointF(10, h + 20), QPointF(
         10 + s, h + 20), QPointF(10 + s / 2, 20)
     # Conversion matrix from cartesian coordinates (x, y, 1) to barycentric coordinates (alpha, beta, gamma)
     self.M = np.array([[self.R.x(), self.G.x(),
                         self.B.x()], [self.R.y(),
                                       self.G.y(),
                                       self.B.y()], [1, 1, 1]])
     self.invM = np.linalg.inv(self.M)
     self.setBackgroundImage()
     # active points
     self.rPoint = activeMixerPoint(self.R.x(),
                                    self.R.y(),
                                    color=Qt.red,
                                    fillColor=Qt.white,
                                    grForm=self)
     self.rPoint.source = self.R
     self.gPoint = activeMixerPoint(self.G.x(),
                                    self.G.y(),
                                    color=Qt.green,
                                    fillColor=Qt.white,
                                    grForm=self)
     self.gPoint.source = self.G
     self.bPoint = activeMixerPoint(self.B.x(),
                                    self.B.y(),
                                    color=Qt.blue,
                                    fillColor=Qt.white,
                                    grForm=self)
     self.bPoint.source = self.B
     graphicsScene = self.scene()
     for point in [self.rPoint, self.gPoint, self.bPoint]:
         graphicsScene.addItem(point)
     gl = QVBoxLayout()
     gl.setAlignment(Qt.AlignTop)
     container = self.addCommandLayout(gl)
     self.values = QLabel()
     vh = QFontMetrics(self.values.font()).height()
     self.values.setMaximumSize(150, vh * 4)  # 4 lines
     gl.addWidget(self.values)
     gl.addWidget(listWidget1)
     # don't commute the 3 next lines !
     self.setDefaults()
     self.adjustSize()
     self.setViewportMargins(0, 0, 0, container.height())
     self.setWhatsThis("""<b>Channel Mixer</b><br>
                     The triangle vertices and the three control points correspond to the R, G, B channels.<br>
                     To <b>mix the channels</b>, drag the 3 control points inside the triangle.
                     The closer a control point is to a vertex, the greater the corresponding channel contribution. <br>
                     To obtain <b>monochrome images</b> only, check the option <i>Monochrome.</i><br>
                     To modify the <b>luminosity channel</b> only (volume mode), check the option <i>Luminosity.</i><br>
                     """)  # end of setWhatsThis
Пример #7
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)
        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,
                dngDict=self.dngDict
            )  # TODO 6/12/19 added keyword dngDict validate
            # 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

        # 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)
        vl1 = QVBoxLayout()
        vl1.addWidget(self.listWidget2)
        gb1 = QGroupBox()
        gb1.setTitle('White Balance')
        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
Пример #8
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(targetImage=targetImage,
                         axeSize=axeSize,
                         layer=layer,
                         parent=parent)
        # 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.clicked.connect(self.resetCurve)
        pushButton2 = QbLUePushButton("Reset R,G,B")
        pushButton2.clicked.connect(self.resetAllCurves)

        # options
        options1 = ['RGB', 'Red', 'Green', 'Blue']
        self.listWidget1 = optionsWidget(options=options1)
        """
        self.listWidget1.setGeometry(0, 0, self.listWidget1.sizeHintForColumn(0) + 5,
                                     self.listWidget1.sizeHintForRow(0)*len(options1) + 5)
        """
        options2 = ['Luminosity']
        self.listWidget2 = optionsWidget(options=options2, exclusive=False)
        self.graphicsScene.options = UDict(
            (self.listWidget1.options, self.listWidget2.options))
        # selection changed handler
        curves = [
            graphicsScene.cubicRGB, graphicsScene.cubicR, graphicsScene.cubicG,
            graphicsScene.cubicB
        ]
        curveDict = dict(zip(options1, 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

        def onSelect2(item):
            itemRGB = self.listWidget1.items[options1[0]]
            if item.isChecked():
                if itemRGB.isChecked():
                    self.listWidget1.unCheckAll()
                    self.listWidget1.checkOption(self.listWidget1.intNames[1])
                itemRGB.setFlags(itemRGB.flags() & ~Qt.ItemIsEnabled)
            else:
                itemRGB.setFlags(itemRGB.flags() | Qt.ItemIsEnabled)
            l = self.scene().layer
            l.applyToStack()
            l.parentImage.onImageChanged()

        self.listWidget2.onSelect = onSelect2

        # set initial selection to RGB
        item = self.listWidget1.items[options1[0]]
        item.setCheckState(Qt.Checked)
        self.listWidget1.select(item)

        # layout
        gl = QGridLayout()
        container = self.addCommandLayout(gl)
        gl.addWidget(self.listWidget1, 0, 0, 2, 1)
        gl.addWidget(self.listWidget2, 0, 1)
        for i, button in enumerate([pushButton1, pushButton2]):
            gl.addWidget(button, i, 2)
        self.adjustSize()
        self.setViewportMargins(0, 0, 0, container.height() + 15)

        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)
Пример #9
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        # positioning window
        self.widgetImg = QLabel(parent=parent)
        # stay on top and center
        self.widgetImg.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.Dialog)
        # source pixmap
        self.sourcePixmap = None
        self.sourcePixmapThumb = None
        # flag indicating where source pixmap come from
        self.sourceFromFile = False
        # opencv flags
        cv2Flag_dict = {
            'Normal Clone': cv2.NORMAL_CLONE,
            'Mixed Clone': cv2.MIXED_CLONE,
            'Monochrome Transfer': cv2.MONOCHROME_TRANSFER
        }
        cv2Flags = list(cv2Flag_dict.keys())

        self.layer.cloningMethod = cv2Flag_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 = True

        self.listWidget1 = optionsWidget(options=cv2Flags,
                                         exclusive=True,
                                         changed=self.dataChanged)
        # init flags
        for i in range(self.listWidget1.count()):
            item = self.listWidget1.item(i)
            item.setData(Qt.UserRole, cv2Flag_dict[item.text()])
        self.options = self.listWidget1.options
        self.listWidget1.checkOption(self.listWidget1.intNames[0])

        optionList2, optionListNames2 = ['opencv', 'blue'
                                         ], ['OpenCV Cloning', 'bLUe Cloning']
        self.listWidget2 = optionsWidget(options=optionList2,
                                         optionNames=optionListNames2,
                                         exclusive=True,
                                         changed=self.dataChanged)
        self.listWidget2.checkOption(self.listWidget2.intNames[1])

        self.options = UDict((self.options, self.listWidget2.options))

        pushButton1 = QPushButton('Load Image From File')

        # Load Image button clicked slot
        def f():
            from bLUeTop.QtGui1 import window
            lastDir = str(window.settings.value('paths/dlgdir', '.'))
            dlg = QFileDialog(window, "select", lastDir,
                              " *".join(IMAGE_FILE_EXTENSIONS))
            if dlg.exec_():
                filenames = dlg.selectedFiles()
                newDir = dlg.directory().absolutePath()
                window.settings.setValue('paths/dlgdir', newDir)
                img = QImage(filenames[0])
                # scale img while keeping its aspect ratio
                # into a QPixmap having the same size as self layer
                sourcePixmap = QPixmap.fromImage(img).scaled(
                    self.layer.size(), Qt.KeepAspectRatio)
                self.sourcePixmap = QPixmap(self.layer.size())
                self.sourcePixmap.fill(Qt.black)
                qp = QPainter(self.sourcePixmap)
                qp.drawPixmap(
                    QRect(0, 0, sourcePixmap.width(), sourcePixmap.height()),
                    sourcePixmap)
                qp.end()
                self.sourcePixmapThumb = self.sourcePixmap.scaled(
                    self.pwSize, self.pwSize, aspectMode=Qt.KeepAspectRatio)
                self.widgetImg.setPixmap(self.sourcePixmapThumb)
                self.widgetImg.setFixedSize(self.sourcePixmapThumb.size())
                self.sourceFromFile = True
            self.widgetImg.show()

        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.listWidget2)
        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 
                            or by another image (e.g. to erase an object):<br>
                               &nbsp; 1) <b> make sure that the cloning layer is the topmost visible layer</b><br>
                               &nbsp; 2) Select the Unmask/FG tool and paint the pixels to erase 
                                         (use the Mask/BG tool to adjust the mask if needed); <br>
                               &nbsp; 3) Select the drag tool and while pressing <b>Ctrl-Alt</b> use
                                         the mouse to drag or zoom the image shown in the painted region;<br>
                            Eventually use <b>Mask Erode</b> from the layer context menu to smooth mask edges.<br>
                            """)  # end of setWhatsthis
Пример #10
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.filterColor = QColor(255, 255, 255)
        self.defaultTemp = sRGBWP  # ref temperature D65
        self.defaultTint = 0

        # options
        optionList, optionNames = [
            'Color Filter', 'Photo Filter', 'Chromatic Adaptation'
        ], ['Color Filter', 'Photo Filter', 'Chromatic Adaptation']
        self.listWidget1 = optionsWidget(
            options=optionList,
            optionNames=optionNames,
            exclusive=True,
            changed=lambda: self.dataChanged.emit())
        self.listWidget1.checkOption(self.listWidget1.intNames[0])
        self.options = self.listWidget1.options
        # link to app color dialog
        self.colorChooser = self.parent().colorChooser
        # color viewer
        self.colorLabel = QLabel()
        self.colorLabel.setMaximumSize(50, 50)
        # color chooser button
        self.colorChooserBtn = QbLUePushButton('Select Filter Color')
        self.colorChooserBtn.clicked.connect(self.showColorChooser)

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

        self.tempLabel = QbLUeLabel()
        self.tempLabel.setMaximumSize(150, 30)
        self.tempLabel.setText("Filter Temperature")
        self.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()))))

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

        # layout
        l = QVBoxLayout()
        l.setAlignment(Qt.AlignTop)
        l.addWidget(self.listWidget1)
        hl2 = QHBoxLayout()
        hl2.addWidget(self.colorLabel)
        hl2.addWidget(self.colorChooserBtn)
        l.addLayout(hl2)
        l.addWidget(self.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 Filter</b> and <b>Photo Filter</b> use 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
Пример #11
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        # positioning window
        self.widgetImg = BWidgetImg(parent=self)
        self.widgetImg.setWindowTitle("Pointing")
        # source
        self.sourceImage = None  # keep for eventual rotation
        self.sourcePixmap = None
        self.sourcePixmapThumb = None
        # flag indicating where source pixmap comes from
        self.layer.sourceFromFile = False
        # opencv flags
        cv2Flag_dict = {
            'Normal Clone': cv2.NORMAL_CLONE,
            'Mixed Clone': cv2.MIXED_CLONE,
            'Monochrome Transfer': cv2.MONOCHROME_TRANSFER
        }
        cv2Flags = list(cv2Flag_dict.keys())

        self.layer.autoclone = True

        self.listWidget1 = optionsWidget(options=cv2Flags,
                                         exclusive=True,
                                         changed=self.dataChanged)
        # init flags
        for i in range(self.listWidget1.count()):
            item = self.listWidget1.item(i)
            item.setData(Qt.UserRole, cv2Flag_dict[item.text()])
        self.options = self.listWidget1.options
        self.listWidget1.checkOption(self.listWidget1.intNames[0])

        optionList2, optionListNames2 = ['opencv', 'blue'
                                         ], ['OpenCV Cloning', 'bLUe Cloning']
        self.listWidget2 = optionsWidget(options=optionList2,
                                         optionNames=optionListNames2,
                                         exclusive=True,
                                         changed=self.dataChanged)
        self.listWidget2.checkOption(self.listWidget2.intNames[1])

        self.options = UDict((self.options, self.listWidget2.options))

        pushButton1 = QPushButton('Load Image From File')

        # Load Image button clicked slot
        def f():
            from bLUeTop.QtGui1 import window
            lastDir = str(window.settings.value('paths/dlgdir', '.'))
            filter = "Images ( *" + " *".join(IMAGE_FILE_EXTENSIONS) + ")"
            dlg = QFileDialog(window, "select", lastDir, filter)
            if dlg.exec_():
                filenames = dlg.selectedFiles()
                newDir = dlg.directory().absolutePath()
                window.settings.setValue('paths/dlgdir', newDir)
                self.sourceImage = QImage(filenames[0])
                self.updateSource()
                """
                # scale img while keeping its aspect ratio
                # into a QPixmap having the same size than self.layer
                sourcePixmap = QPixmap.fromImage(self.sourceImage).scaled(self.layer.size(), Qt.KeepAspectRatio)
                self.sourceSize = sourcePixmap.size()
                self.sourcePixmap = QPixmap(self.layer.size())
                self.sourcePixmap.fill(Qt.black)
                qp = QPainter(self.sourcePixmap)
                qp.drawPixmap(QPointF(), sourcePixmap)  # (QRect(0, 0, sourcePixmap.width(), sourcePixmap.height()), sourcePixmap)
                qp.end()
                self.sourcePixmapThumb = self.sourcePixmap.scaled(self.pwSize, self.pwSize, aspectMode=Qt.KeepAspectRatio)
                self.widgetImg.setPixmap(self.sourcePixmapThumb)
                self.widgetImg.setFixedSize(self.sourcePixmapThumb.size())
                self.layer.sourceFromFile = True
                """
            self.widgetImg.show()

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

        # reset button clicked slot
        def g():
            layer = self.layer
            # mask all pixels
            layer.resetMask(maskAll=True)
            layer.setMaskEnabled(color=False)
            # reset cloning layer
            layer.xAltOffset, layer.yAltOffset = 0.0, 0.0
            layer.AltZoom_coeff = 1.0
            layer.cloningState = ''
            layer.applyCloning(seamless=False, showTranslated=True)
            layer.parentImage.onImageChanged()

        pushButton2.clicked.connect(g)

        pushButton3 = QPushButton('Rotate Image')

        def h():
            self.sourceImage = self.sourceImage.transformed(
                QTransform().rotate(90))
            self.updateSource()

        pushButton3.clicked.connect(h)

        # layout
        layout = QVBoxLayout()
        layout.setContentsMargins(20, 0, 20, 25)  # left, top, right, bottom
        self.setLayout(layout)
        layout.addWidget(self.listWidget2)
        layout.addWidget(self.listWidget1)
        hl = QHBoxLayout()
        hl.addWidget(pushButton1)
        hl.addWidget(pushButton3)
        hl.addWidget(pushButton2)
        layout.addLayout(hl)

        self.setDefaults()

        self.setWhatsThis("""
                            <b>Cloning/healing brush</b><br>
                            Seamless replacement of a region of the image by another region of the same image 
                            or by another image (e.g. to erase an object):<br>
                               &nbsp; 1) <b> Make sure that the cloning layer is the topmost visible layer</b><br>
                               &nbsp; 2) With the <i>Pointer Tool</i> selected, <b>Ctrl+Alt+Click</b>
                                          on the layer or the pointing window to mark the source starting point;<br> 
                               &nbsp; 3) Select the <i>Unmask/FG Tool</i> and paint the destination region to copy and clone pixels. 
                                         Use <i>the Mask/BG Tool</i> to adjust the mask if needed. <br>
                            Use <b>Ctrl+Alt+Mouse Wheel</b> to zoom in or out the cloned region.<br>
                            Eventually use <b>Mask Erode</b> from the layer context menu to smooth the contour of the mask.<br>
                            """)  # end of setWhatsthis
Пример #12
0
    def __init__(self,
                 targetImage=None,
                 axeSize=500,
                 layer=None,
                 parent=None,
                 curveType='quadric'):
        super().__init__(targetImage=targetImage,
                         axeSize=axeSize,
                         layer=layer,
                         parent=parent)
        graphicsScene = self.scene()
        # init the curve
        if curveType == 'quadric':
            curve = activeQuadricSpline(graphicsScene.axeSize)
        else:
            curve = activeCubicSpline(graphicsScene.axeSize)
        graphicsScene.addItem(curve)
        graphicsScene.quadricB = curve
        curve.channel = channelValues.Br
        curve.histImg = graphicsScene.layer.inputImg().histogram(
            size=graphicsScene.axeSize,
            bgColor=graphicsScene.bgColor,
            range=(0, 255),
            chans=channelValues.Br)  # , mode='Luminosity')
        curve.initFixedPoints()
        # set current curve
        graphicsScene.cubicItem = graphicsScene.quadricB
        graphicsScene.cubicItem.setVisible(True)
        self.setWhatsThis("""<b>Contrast Curve</b><br>
                        Drag <b>control points</b> and <b>tangents</b> with the mouse.<br>
                        <b>Add</b> a control point by clicking on the curve.<br>
                        <b>Remove</b> a control point by clicking it.<br>
                        <b>Zoom</b> with the mouse wheel.<br>
                        """)

        def onResetCurve():
            """
            Reset the selected curve
            """
            self.scene().cubicItem.reset()
            l = self.scene().layer
            l.applyToStack()
            l.parentImage.onImageChanged()

        # buttons
        pushButton1 = QbLUePushButton("Reset to Auto Curve")
        pushButton1.setGeometry(10, 20, 100, 30)  # x,y,w,h
        pushButton1.clicked.connect(onResetCurve)
        if curveType == 'quadric':
            optionList1, optionNames1 = ['Show Tangents'], ['Show Tangents']
            self.listWidget1 = optionsWidget(options=optionList1,
                                             optionNames=optionNames1,
                                             exclusive=False)

            def f():
                curve.setTangentsVisible(
                    self.listWidget1.options['Show Tangents'])

            self.listWidget1.itemClicked.connect(f)

        # layout
        gl = QHBoxLayout()
        container = self.addCommandLayout(gl)
        if curveType == 'quadric':
            gl.addWidget(self.listWidget1)
        gl.addWidget(pushButton1)
        self.adjustSize()
        self.setViewportMargins(0, 0, 0, container.height() + 15)

        graphicsScene.addWidget(pushButton1)
        self.pushButton = pushButton1
Пример #13
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()
Пример #14
0
    def __init__(self, targetImage=None, size=200, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.mode = 'Luminosity'
        self.chanColors = [Qt.gray]
        self.setWindowTitle('Histogram')
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(size, 100)
        self.Label_Hist = QLabel()
        self.Label_Hist.setScaledContents(True)
        self.Label_Hist.setFocusPolicy(Qt.ClickFocus)
        self.setStyleSheet("QListWidget{border: 0px; font-size: 12px}")

        # options
        options1, optionNames1 = ['Original Image'], ['Source']
        self.listWidget1 = optionsWidget(options=options1,
                                         optionNames=optionNames1,
                                         exclusive=False)
        self.listWidget1.setFixedSize(
            (self.listWidget1.sizeHintForColumn(0) + 15) * len(options1), 20)
        options2, optionNames2 = ['R', 'G', 'B', 'L'], ['R', 'G', 'B', 'L']
        self.listWidget2 = optionsWidget(options=options2,
                                         optionNames=optionNames2,
                                         exclusive=False,
                                         flow=optionsWidget.LeftToRight)
        #self.listWidget2.setFixedSize((self.listWidget2.sizeHintForRow(0) + 15) * len(options2), 20)
        self.listWidget2.setFixedSize(
            (self.listWidget2.sizeHintForRow(0) + 20) * len(options2),
            20)  # + 20 needed to prevent scroll bar on ubuntu
        # default: show color hists only
        for i in range(3):
            self.listWidget2.checkOption(self.listWidget2.intNames[i])
        self.options = UDict(
            (self.listWidget1.options, self.listWidget2.options))
        self.setWhatsThis("""
        <b>Histogram</b><br>
        The histogram shows the initial or final color ditribution of the image, depending on 
        whether the <I>Source</I> option is checked or not. 
        """)

        def onSelect(item):
            try:
                self.targetImage.onImageChanged()
                self.Label_Hist.update()
            except AttributeError:
                return

        self.listWidget1.onSelect = onSelect
        self.listWidget2.onSelect = onSelect

        # layout
        h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 2)
        h.addStretch(1)
        h.addWidget(self.listWidget1)
        h.addStretch(1)
        h.addWidget(self.listWidget2)
        h.addStretch(1)
        vl = QVBoxLayout()
        #vl.setAlignment(Qt.AlignTop)  prevent the histogram from stretching vertically
        vl.addWidget(self.Label_Hist)
        vl.addLayout(h)
        vl.setContentsMargins(0, 0, 0, 2)  # left, top, right, bottom
        self.setLayout(vl)
        self.adjustSize()
Пример #15
0
    def __init__(self, targetImage=None, axeSize=500, layer=None, parent=None):
        super().__init__(targetImage=targetImage, axeSize=axeSize, layer=layer, parent=parent)
        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.adjustSize()
        pushButton1.clicked.connect(self.resetCurve)
        pushButton2 = QPushButton("Reset All")
        pushButton2.adjustSize()
        pushButton2.clicked.connect(self.resetAllCurves)

        # options
        options = ['L', 'a', 'b']
        self.listWidget1 = optionsWidget(options=options, exclusive=True)
        self.listWidget1.setMinimumSize(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):
            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)

        # 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)
Пример #16
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
Пример #17
0
    def __init__(self, targetImage=None, size=200, layer=None, parent=None):
        super().__init__(layer=layer, targetImage=targetImage, parent=parent)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setMinimumSize(size, 100)
        self.Label_Hist = QLabel()
        self.Label_Hist.setFocusPolicy(Qt.ClickFocus)
        self.Label_Hist.setMaximumSize(140000, 140000)

        self.Label_Hist.setSizePolicy(QSizePolicy.Preferred, 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}
        self.setWhatsThis("""
        <b>Image Histogram</b><br>
        The histogram shows the color ditributions for the final image unless
        the <I>Source Image</I> option is checked. 
        """)

        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)
        self.adjustSize()
Пример #18
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