class patchForm(baseForm): """ Seamless cloning form. """ # positioning window size pwSize = 200 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> 1) <b> Make sure that the cloning layer is the topmost visible layer</b><br> 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> 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 def setDefaults(self): self.enableOptions() self.listWidget1.checkOption(self.listWidget1.intNames[0]) self.layer.cloningMethod = self.listWidget1.checkedItems[0].data( Qt.UserRole) self.layer.setMaskEnabled() # TODO added 18/12/19 validate self.layer.resetMask( maskAll=True) # , alpha=128) # TODO added 18/12/19 validate # self.widgetImg.setPixmap(QPixmap.fromImage(self.layer.inputImg().scaled(200, 200, aspectMode=Qt.KeepAspectRatio))) # init positioning window img = self.layer.inputImg( drawTranslated=False ) # TODO added drawTranslated 16/12/19 validate if img.rPixmap is None: img.rPixmap = QPixmap.fromImage(img) self.sourcePixmap = img.rPixmap self.sourcePixmapThumb = self.sourcePixmap.scaled( 200, 200, aspectMode=Qt.KeepAspectRatio) self.widgetImg.setPixmap(self.sourcePixmapThumb) self.widgetImg.setFixedSize(self.sourcePixmapThumb.size()) # show positioning window self.widgetImg.hide() try: self.dataChanged.disconnect() except RuntimeError: pass self.dataChanged.connect(self.updateLayer) def updateSource(self): """ sets the pointing window, using self.sourceImage """ # 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 def enableOptions(self): if self.options['blue']: # make sure disabled options are unchecked self.listWidget1.checkOption('Normal Clone') for item in [self.listWidget1.item(i) for i in (1, 2)]: # mixed clone, monochrome transfer if self.options['opencv']: item.setFlags(item.flags() | Qt.ItemIsEnabled) else: item.setFlags(item.flags() & ~Qt.ItemIsEnabled) def updateLayer(self): """ data changed slot """ self.enableOptions() layer = self.layer layer.cloningMethod = self.listWidget1.checkedItems[0].data( Qt.UserRole) layer.applyToStack() layer.parentImage.onImageChanged()
class Picture(object): def __init__(self, path): super(Picture, self).__init__() self.path = path self.name = self.path.split("/")[-1] self.extension = os.path.splitext(self.path)[1] self.scale = 1.0 self.image = QImage(self.path) self.thumbnail = self.image.scaled(QSize(110, 110), aspectMode=Qt.KeepAspectRatio, mode=Qt.SmoothTransformation) self.resolution = "Resolution: " + str(self.image.width()) + "x" + str( self.image.height()) + "px" def deletePicture(self): os.remove(self.path) def verticalFlip(self): self.image = self.image.mirrored(vertically=True, horizontally=False) self.thumbnail = self.image.scaled(QSize(110, 110), aspectMode=Qt.KeepAspectRatio, mode=Qt.SmoothTransformation) self.image.save(self.path) def horizontalFlip(self): self.image = self.image.mirrored(horizontally=True, vertically=False) self.thumbnail = self.image.scaled(QSize(110, 110), aspectMode=Qt.KeepAspectRatio, mode=Qt.SmoothTransformation) self.image.save(self.path) def zoomIn(self): self.scale = self.scale * 0.9 def zoomOut(self): self.scale = self.scale * 1.1 def rotateCW(self): transform = QTransform() transform.translate(self.image.width() / 2, self.image.height() / 2) transform.rotate(90) self.image = self.image.transformed(transform) self.image.save(self.path) self.thumbnail = self.image.scaled(QSize(110, 110), aspectMode=Qt.KeepAspectRatio, mode=Qt.SmoothTransformation) def rotateCCW(self): transform = QTransform() transform.translate(self.image.width() / 2, self.image.height() / 2) transform.rotate(-90) self.image = self.image.transformed(transform) self.image.save(self.path) self.thumbnail = self.image.scaled(QSize(110, 110), aspectMode=Qt.KeepAspectRatio, mode=Qt.SmoothTransformation) def saveImage(self): self.image.save(self.path)
class QLayer(vImage): """ Base class for image layers """ @classmethod def fromImage(cls, mImg, parentImage=None): """ Returns a QLayer object initialized with mImg. @param mImg: @type mImg: QImage @param parentImage: @type parentImage: mImage @return: @rtype: Qlayer """ layer = QLayer(QImg=mImg, parentImage=parentImage) layer.parentImage = parentImage return layer def __init__(self, *args, **kwargs): ############################################################ # Signals # Making QLayer inherit from QObject leads to # a bugged behavior of hasattr and getattr. # So, we don't add signals as first level class attributes. # Instead, we use instances of ad hoc signal containers. ############################################################ self.visibilityChanged = baseSignal_bool() self.colorPicked = baseSignal_Int2() self.selectionChanged = baseSignal_No() self.maskSettingsChanged = baseSignal_No() ########################################################### # when a geometric transformation is applied to the whole image # each layer must be replaced with a transformed layer, recorded in tLayer # and tLayer.parentLayer keeps a reference to the original layer. ########################################################### self.tLayer = self self.parentLayer = self self.modified = False self.name = 'noname' self.visible = True self.isClipping = False self.role = kwargs.pop('role', '') self.tool = None # back link to parent image parentImage = kwargs.pop('parentImage', None) self.parentImage = weakProxy(parentImage) super().__init__(*args, **kwargs) # don't move backwards # mask init, must be done after after calling super().__init__ if type(self) not in [QPresentationLayer]: self.mask = QImage(self.width(), self.height(), QImage.Format_ARGB32) # default : unmask all self.mask.fill(self.defaultColor_UnMasked) # layer opacity, range 0.0...1.0 self.opacity = 1.0 self.compositionMode = QPainter.CompositionMode_SourceOver # The next two attributes are used by adjustment layers only. self.execute = lambda l=None, pool=None: l.updatePixmap( ) if l is not None else None self.options = {} # actionName is used by methods graphics***.writeToStream() self.actionName = 'actionNull' # view is the dock widget containing # the graphics form associated with the layer self.view = None # undo/redo mask history self.historyListMask = historyList(size=5) # consecutive layers can be grouped. # A group is a list of QLayer objects self.group = [] # layer offsets self.xOffset, self.yOffset = 0, 0 self.Zoom_coeff = 1.0 # clone dup layer shift and zoom relative to current layer self.xAltOffset, self.yAltOffset = 0, 0 self.AltZoom_coeff = 1.0 self.updatePixmap() def getGraphicsForm(self): """ Return the graphics form associated with the layer @return: @rtype: QWidget """ if self.view is not None: return self.view.widget() return None def isActiveLayer(self): if self.parentImage.getActiveLayer() is self: return True return False def getMmcSpline(self): """ Returns the spline used for multimode contrast correction if it is initialized, and None otherwise. @return: @rtype: activeSpline """ # get layer graphic form grf = self.getGraphicsForm() # manual curve form if grf.contrastForm is not None: return grf.contrastForm.scene().cubicItem return None def addTool(self, tool): """ Adds tool to layer @param tool: @type tool: rotatingTool """ self.tool = tool tool.modified = False tool.layer = self try: tool.layer.visibilityChanged.sig.disconnect( ) # TODO signals removed 30/09/18 validate except RuntimeError: pass tool.layer.visibilityChanged.sig.connect( tool.setVisible) # TODO signals removed 30/09/18 validate tool.img = self.parentImage w, h = tool.img.width(), tool.img.height() for role, pos in zip( ['topLeft', 'topRight', 'bottomRight', 'bottomLeft'], [QPoint(0, 0), QPoint(w, 0), QPoint(w, h), QPoint(0, h)]): tool.btnDict[role].posRelImg = pos tool.btnDict[role].posRelImg_ori = pos tool.btnDict[role].posRelImg_frozen = pos tool.moveRotatingTool() def setVisible(self, value): """ Sets self.visible to value and emit visibilityChanged.sig @param value: @type value: bool """ self.visible = value self.visibilityChanged.sig.emit( value) # TODO signals removed 30/09/18 validate def bTransformed(self, transformation, parentImage): """ Apply transformation to a copy of layer. Returns the transformed copy. @param transformation: @type transformation: QTransform @param parentImage: @type parentImage: vImage @return: transformed layer @rtype: QLayer """ # init a new layer from transformed image : # all static attributes (caches...) are reset to default, but thumb tLayer = QLayer.fromImage(self.transformed(transformation), parentImage=parentImage) # copy dynamic attributes from old layer for a in self.__dict__.keys(): if a not in tLayer.__dict__.keys(): tLayer.__dict__[a] = self.__dict__[a] tLayer.name = self.name tLayer.actionName = self.actionName tLayer.view = self.view tLayer.visible = self.visible # TODO added 25/09/18 validate # cut link from old layer to graphic form # self.view = None # TODO 04/12/17 validate tLayer.execute = self.execute tLayer.mask = self.mask.transformed(transformation) tLayer.maskIsEnabled, tLayer.maskIsSelected = self.maskIsEnabled, self.maskIsSelected return tLayer def initThumb(self): """ Override vImage.initThumb, to set the parentImage attribute """ super().initThumb() self.thumb.parentImage = self.parentImage def initHald(self): """ Build a hald image (as a QImage) from identity 3D LUT. """ if not self.cachesEnabled: return s = int(LUT3DIdentity.size**(3.0 / 2.0)) + 1 buf0 = LUT3DIdentity.toHaldArray(s, s).haldBuffer # self.hald = QLayer(QImg=QImage(QSize(190,190), QImage.Format_ARGB32)) self.hald = QImage(QSize(s, s), QImage.Format_ARGB32) buf1 = QImageBuffer(self.hald) buf1[:, :, :3] = buf0 buf1[:, :, 3] = 255 self.hald.parentImage = self.parentImage def getHald(self): if not self.cachesEnabled: s = int(LUT3DIdentity.size**(3.0 / 2.0)) + 1 buf0 = LUT3DIdentity.toHaldArray(s, s).haldBuffer # self.hald = QLayer(QImg=QImage(QSize(190,190), QImage.Format_ARGB32)) hald = QImage(QSize(s, s), QImage.Format_ARGB32) buf1 = QImageBuffer(hald) buf1[:, :, :3] = buf0 buf1[:, :, 3] = 255 hald.parentImage = self.parentImage return hald if self.hald is None: self.initHald() return self.hald def getCurrentImage(self): """ Returns current (full, preview or hald) image, according to the value of the flags useThumb and useHald. The thumbnail and hald are computed if they are not initialized. Otherwise, they are not updated unless self.thumb is None or purgeThumb is True. Overrides vImage method @return: current image @rtype: QLayer """ if self.parentImage.useHald: return self.getHald() if self.parentImage.useThumb: return self.getThumb() else: return self def inputImg(self, redo=True): """ return maskedImageContainer/maskedThumbContainer. If redo is True(default), containers are updated. layer.applyToStack() always calls inputImg() with redo=True. So, to keep the containers up to date we only have to follow each layer modification with a call to layer.applyToStack(). @param redo: @type redo: boolean @return: @rtype: bImage """ lower = self.parentImage.layersStack[self.getLowerVisibleStackIndex()] container = lower.maskedThumbContainer if self.parentImage.useThumb else lower.maskedImageContainer if redo or container is None: container = lower.getCurrentMaskedImage() container.rPixmap = None # invalidate and don't update else: if container.rPixmap is None: container.rPixmap = QPixmap.fromImage( container) # will probably be reused : update return container def full2CurrentXY(self, x, y): """ Maps x,y coordinates of pixel in the full image to coordinates in current image. @param x: @type x: int or float @param y: @type y: int or float @return: @rtype: 2uple of int """ if self.parentImage.useThumb: currentImg = self.getThumb() x = (x * currentImg.width()) / self.width() y = (y * currentImg.height()) / self.height() return int(x), int(y) def getCurrentMaskedImage(self): """ Blend the layer stack up to self (included), taking into account the masks. The method uses the non color managed rPixmap to build the masked image. For convenience, mainly to be able to use its color space buffers, the built image is of type bImage. It is drawn on a container image, instantiated only once. @return: masked image @rtype: bImage """ # init containers if needed. They are instantiated only # once and updated by drawing. if self.parentImage.useHald: return self.getHald() if self.maskedThumbContainer is None: self.maskedThumbContainer = bImage.fromImage( self.getThumb(), parentImage=self.parentImage) if self.maskedImageContainer is None: self.maskedImageContainer = bImage.fromImage( self, parentImage=self.parentImage) if self.parentImage.useThumb: img = self.maskedThumbContainer else: img = self.maskedImageContainer # draw lower stack qp = QPainter(img) top = self.parentImage.getStackIndex(self) bottom = 0 for i, layer in enumerate(self.parentImage.layersStack[bottom:top + 1]): if layer.visible: if i == 0: qp.setCompositionMode(QPainter.CompositionMode_Source) else: qp.setOpacity(layer.opacity) qp.setCompositionMode(layer.compositionMode) if layer.rPixmap is None: layer.rPixmap = QPixmap.fromImage(layer.getCurrentImage( )) # TODO modified 9/12/18 validate qp.drawPixmap(QRect(0, 0, img.width(), img.height()), layer.rPixmap) # clipping if layer.isClipping and layer.maskIsEnabled: # draw mask as opacity mask # mode DestinationIn (set dest opacity to source opacity) qp.setCompositionMode( QPainter.CompositionMode_DestinationIn) omask = vImage.color2OpacityMask(layer.mask) qp.drawImage(QRect(0, 0, img.width(), img.height()), omask) qp.end() return img def applyToStack(self): """ Apply new layer parameters and propagate changes to upper layers. """ # recursive function def applyToStack_(layer, pool=None): # apply transformation if layer.visible: start = time() layer.execute(l=layer) layer.cacheInvalidate() print("%s %.2f" % (layer.name, time() - start)) stack = layer.parentImage.layersStack lg = len(stack) ind = layer.getStackIndex() + 1 # update histograms displayed # on the layer form, if any if ind < lg: grForm = stack[ind].getGraphicsForm() if grForm is not None: grForm.updateHists() # get next upper visible layer while ind < lg: if stack[ind].visible: break ind += 1 if ind < lg: layer1 = stack[ind] applyToStack_(layer1, pool=pool) try: QApplication.setOverrideCursor(Qt.WaitCursor) QApplication.processEvents() applyToStack_(self, pool=None) # update the presentation layer self.parentImage.prLayer.execute(l=None, pool=None) finally: self.parentImage.setModified(True) QApplication.restoreOverrideCursor() QApplication.processEvents() """ def applyToStackIter(self): #iterative version of applyToStack stack = self.parentImage.layersStack ind = self.getStackIndex() + 1 try: QApplication.setOverrideCursor(Qt.WaitCursor) QApplication.processEvents() self.execute() for layer in stack[ind:]: if layer.visible: layer.cacheInvalidate() # for hald friendly layer compute output hald, otherwise compute output image layer.execute() finally: QApplication.restoreOverrideCursor() QApplication.processEvents() """ def isAdjustLayer(self): return self.view is not None # hasattr(self, 'view') def isSegmentLayer(self): return 'SEGMENT' in self.role def isCloningLayer(self): return 'CLONING' in self.role def isGeomLayer(self): return 'GEOM' in self.role def is3DLUTLayer(self): return '3DLUT' in self.role def isRawLayer(self): return 'RAW' in self.role def updatePixmap(self, maskOnly=False): """ Synchronize rPixmap with the layer image and mask. if maskIsEnabled is False, the mask is not used. If maskIsEnabled is True, then - if maskIsSelected is True, the mask is drawn over the layer as a color mask. - if maskIsSelected is False, the mask is drawn as an opacity mask, setting the image opacity to that of the mask (mode DestinationIn). @param maskOnly: not used : for consistency with overriding method signature @type maskOnly: boolean """ rImg = self.getCurrentImage() # apply layer transformation. Missing pixels are set to QColor(0,0,0,0) if self.xOffset != 0 or self.yOffset != 0: x, y = self.full2CurrentXY(self.xOffset, self.yOffset) rImg = rImg.copy( QRect(-x, -y, rImg.width() * self.Zoom_coeff, rImg.height() * self.Zoom_coeff)) if self.maskIsEnabled: rImg = vImage.visualizeMask(rImg, self.mask, color=self.maskIsSelected, clipping=True) # self.isClipping) self.rPixmap = QPixmap.fromImage(rImg) self.setModified(True) def getStackIndex(self): """ Returns layer index in the stack, len(stack) - 1 if the layer is not in the stack. @return: @rtype: int """ i = -1 # TODO added 5/11/18 validate for i, l in enumerate(self.parentImage.layersStack): if l is self: break return i def setMaskEnabled(self, color=False): self.maskIsEnabled = True self.maskIsSelected = color self.maskSettingsChanged.sig.emit() def getTopVisibleStackIndex(self): """ Returns the index of the top visible layer @return: @rtype: """ stack = self.parentImage.layersStack lg = len(stack) for i in range(lg - 1, -1, -1): if stack[i].visible: return i return -1 def getLowerVisibleStackIndex(self): """ Returns the index of the next lower visible layer, -1 if it does not exists @return: @rtype: int """ ind = self.getStackIndex() stack = self.parentImage.layersStack for i in range(ind - 1, -1, -1): if stack[i].visible: return i return -1 def getUpperVisibleStackIndex(self): """ Returns the index of the next upper visible layer, -1 if it does not exists @return: @rtype: int """ ind = self.getStackIndex() stack = self.parentImage.layersStack lg = len(stack) for i in range(ind + 1, lg, 1): if stack[i].visible: return i return -1 def getLowerClippingStackIndex(self): """ Returns the index of the next lower clipping layer, -1 if it does not exists @return: @rtype: int """ ind = self.getStackIndex() for i in range(ind + 1, len(self.parentImage.layersStack), 1): if self.parentImage.layersStack[i].isClipping: return i return -1 def linkMask2Lower(self): """ share mask with next lower layer @return: @rtype: """ ind = self.getStackIndex() if ind == 0: return lower = self.parentImage.layersStack[ind - 1] # don't link two groups if self.group and lower.group: return if not self.group and not lower.group: self.group = [self, lower] lower.group = self.group elif not lower.group: if not any(o is lower for o in self.group): self.group.append(lower) lower.group = self.group elif not self.group: if not any(item is self for item in lower.group): lower.group.append(self) self.group = lower.group self.mask = lower.mask def unlinkMask(self): self.mask = self.mask.copy() # remove self from group for i, item in enumerate(self.group): if item is self: self.group.pop(i) # don't keep group with length 1 if len(self.group) == 1: self.group.pop(0) break self.group = [] def merge_with_layer_immediately_below(self): """ Merges a layer with the next lower visible layer. Does nothing if mode is preview or the target layer is an adjustment layer. """ if not hasattr(self, 'inputImg'): return ind = self.getLowerVisibleStackIndex() if ind < 0: # no visible layer found return target = self.parentImage.layersStack[ind] if hasattr(target, 'inputImg') or self.parentImage.useThumb: info = "Uncheck Preview first" if self.parentImage.useThumb else "Target layer must be background or image" dlgWarn("Cannot Merge layers", info=info) return # update stack self.parentImage.layersStack[0].applyToStack() # merge # target.setImage(self) qp = QPainter(target) qp.setCompositionMode(self.compositionMode) qp.setOpacity(self.opacity) qp.drawImage(QRect(0, 0, self.width(), self.height()), self) target.updatePixmap() self.parentImage.layerView.clear(delete=False) currentIndex = self.getStackIndex() self.parentImage.activeLayerIndex = ind self.parentImage.layersStack.pop(currentIndex) self.parentImage.layerView.setLayers(self.parentImage) def reset(self): """ reset layer to inputImg """ self.setImage(self.inputImg()) def setOpacity(self, value): """ set layer opacity to value/100.0 @param value: @type value: int in range 0..100 """ self.opacity = value / 100.0 def setColorMaskOpacity(self, value): """ Set mask alpha channel to value @param value: @type value: int in range 0..255 """ self.colorMaskOpacity = value buf = QImageBuffer(self.mask) buf[:, :, 3] = np.uint8(value) def readFromStream(self, dataStream): grForm = self.getGraphicsForm() if grForm is not None: grForm.readFromStream(dataStream) return dataStream