--- Title: PyQt で画像のビューアを作る 10 Author: yamasyuh68 Web: https://mimemo.io/m/xn0Yd4ZWReGy7v6 --- 190707 - 現時点の全コード - 保存機能をつけたので基本部分を結構書き直したけど実用出来るようになった @[TOC](もくじなり) https://live.staticflickr.com/65535/48219295462_c394ed6480_b.jpg --- # enhancer.py ``` import io ,os from PyQt5.QtGui import QPixmap, QRadialGradient,QBrush , QColor,QPainter from PyQt5.QtWidgets import QApplication,QWidget ,QMainWindow,QColorDialog from PyQt5.QtCore import Qt, QBuffer , QIODevice from PIL import Image, ImageQt,ImageFilter,ImageChops,ImageOps ,ImageEnhance import enhancer_ui class MainWindow(QMainWindow): def __init__(self): super().__init__() self.ui=enhancer_ui.Ui_MainWindow() self.ui.setupUi(self) self.ui.bezier.initial(self) self.value=0 self.setAcceptDrops(True) self.fname= self.img = Image.open( self.fname ) # original self.mask = None # mask(grad) self.current=self.img.copy() # current image(showing) self.enhanced=self.img.copy() # current before mask self.showimage( ) self.ui.label_5.setgrad( self) if (self.ui.label.height() /self.ui.label.width() > self.img.height/self.img.width) : if self.ui.label.height() > self.img.height : print('width no') # 横長 else: print('width long') else: if self.ui.label.width() > self.img.width : print('height no') # 縦長 else: print('height long') def GetColor(self): # カラーダイアログを出すだけ QColorDialog().getColor(Qt.white) def dragEnterEvent(self, e): # これ必要 ここでacceptしてやる if e.mimeData().hasFormat('text/plain'): e.accept() else: # e.ignore() e.accept() def dropEvent(self, e): # ここで受け取る self.fname=e.mimeData().text()[8:] self.img = Image.open( self.fname) self.imagereset() def bezierOp(self,LUT): # ベジェクラスからLUTをもらう LUT = [ -( i-270) for i in LUT ] im = self.img.convert('RGB') size = im.size im2 = Image.new('RGB',size) data = im.getdata() # r,g,b = data[l] # r = LUT[r] # g = LUT[g] # b = LUT[b] a = self.ui.radioButton_5.isChecked() b = self.ui.radioButton_6.isChecked() c = self.ui.radioButton_7.isChecked() if a and b and c: data2=[( LUT[i[0]] , LUT[i[1]] , LUT[i[2]] ) for i in data ] elif not a and b and c: data2=[( i[0] , LUT[i[1]] , LUT[i[2]] ) for i in data ] elif a and not b and not c: # a only data2=[( LUT[i[0]] , i[1] , i[2] ) for i in data ] elif not a and not b and c: # c only data2=[( i[0] , i[1] , LUT[i[2]] ) for i in data ] elif not a and b and not c: # b only data2=[( i[0] , LUT[i[1]] , i[2] ) for i in data ] else: return im2.putdata(data2) self.enhanced=im2.copy() del im2 self.showimage( ) def showimage ( self ): # if self.ui.radioButton.isChecked(): copy=self.img.copy() copy.paste(self.enhanced , (0,0), self.mask ) self.current = copy.convert('RGBA') del copy else: self.current = self.enhanced.copy() buffer = io.BytesIO() self.current.save( buffer , "BMP") pix = QPixmap() pix.loadFromData( buffer.getvalue() ) self.ui.label.setPixmap(pix) self.ui.label.setAlignment ( Qt.AlignTop ) self.ui.label.show() del buffer self.ui.widget_2.setimage(self.enhanced) # histogram def maskchaged(self ): # Qpixmap --> pillow.Image :(L) buffer = QBuffer() buffer.open(QIODevice.WriteOnly) self.ui.label_5.pixmap().save(buffer,'BMP') self.mask = Image.open( io.BytesIO( buffer.data() ) ).convert('L') self.showimage() def imagefix(self): self.img=self.current.copy() self.imagereset() def imagerestore(self): self.img = Image.open( self.fname ) # original self.imagereset() def imagesave(self): path,ext =os.path.splitext(self.fname) fname=path+'.png' # alway save png while os.path.exists(fname): path,ext =os.path.splitext(fname) path += '_' fname = path+ext self.current.save(fname) def imagereset(self): # self.current=self.img.copy() self.enhanced=self.img.copy() self.ui.bezier.reset() self.showimage() self.ui.label_5.setgrad( self ) self.ui.widget_2.setimage(self.img) # histogram self.ui.widget.setimage(self.img) # histogram def test(self,v): # orig=Image.open(< pass >) grad=Image.open().convert('L') orig_back=self.img.copy() orig_conv=self.img.point(lambda x: x * (v/50)) orig_back.paste(orig_conv, (0,0), grad ) pp=orig_back.convert('RGBA') pm = QPixmap.fromImage(ImageQt.ImageQt(pp)) self.ui.label.setPixmap(pm) self.ui.label.show() def radiochecked(self): # right : apply mask self.showimage() def radiotoggle1(self,flag): # left radio : sent flag to class self.ui.label_5.setflag(flag) def slider_1(self,v): # bright self.value=v v=v/100 con9 = ImageEnhance.Brightness(self.img) self.enhanced = con9.enhance(v) # 1が基本 self.showimage( ) def slider_2(self,v): # rotate expand=True v -= 100 self.enhanced= self.img.rotate(v ,resample=Image.BICUBIC ) self.showimage( ) def slider_5(self,v): # シャープ self.value=v v=v/100 con9 = ImageEnhance.Sharpness(self.img) self.enhanced = con9.enhance(v) # 1が基本 self.showimage( ) def bye(self): quit() if __name__ == "__main__": import sys app = QApplication(sys.argv) win = MainWindow() win.show() sys.exit(app.exec_()) ``` # enhancer_subclass.py ``` from PyQt5.QtCore import Qt , QPoint,QPointF,QRectF from PyQt5.QtGui import QPixmap , QPainter , QRadialGradient,QBrush , QColor,QPen,QPainterPath from PyQt5.QtWidgets import QLabel,QWidget from PIL import Image import io class gradlabel(QLabel): def __init__(self , parent): super().__init__(parent) # self.oya = parent self.oya=None self.flag = 0 # mouse flag self.gradflag = 0 # mask flag self.pos=QPoint() self.grad=None self.list=[] self.slider=None def setgrad(self,q): # initialize self.oya=q # self.slider=p # self.pix=q.img buffer = io.BytesIO() q.img.save( buffer , "BMP") self.pix = QPixmap() # .loadFromData( buffer.getvalue() ) self.pix.loadFromData( buffer.getvalue() ) self.setPixmap(self.pix) del buffer self.setAlignment ( Qt.AlignTop ) self.grad=QPixmap(self.pix.width() ,self.pix.height() ) self.list=[ self.pix.width()/2 , self.pix.height()/2 , # center of circle self.pix.width()/2 , # 半径 self.pix.width()/2 , self.pix.height()/2 # center of grad ] def setflag(self , flag): # from parent radio self.gradflag=flag self.draw() def mousePressEvent(self, event): if event.button() == Qt.LeftButton : self.flag = 1 elif event.button() == Qt.MidButton : self.flag = 2 self.pos = event.pos() elif event.button() == Qt.RightButton : self.flag = 3 else : self.flag = 0 self.mouseMoveEvent(event) def mouseMoveEvent(self, event): if self.flag == 2 : self.list[0] = event.pos().x() self.list[1] = event.pos().y() self.list[3] = event.pos().x() self.list[4] = event.pos().y() elif self.flag==3: self.list[3] = event.pos().x() self.list[4] = event.pos().y() elif self.flag==1: self.list[0] = event.pos().x() self.list[1] = event.pos().y() else : return self.draw() def mouseReleaseEvent(self, event): self.flag = 0 def wheelEvent (self, e):# mousewheel 引いたら-yが128 if e.angleDelta().y() < 0 : # increase self.list[2] +=1 else: self.list[2] -=1 self.draw() def draw(self): if self.gradflag: self.setPixmap(self.grad) paint=QPainter() paint.begin( self.grad ) # <---- grad = QRadialGradient( self.list[0] , self.list[1] , self.list[2] , self.list[3] , self.list[4]) grad.setColorAt(0.0, QColor(Qt.white)) #色を指定 grad.setColorAt(1.0, QColor(Qt.black)) paint.setBrush(QBrush(grad)) #画面への描画 paint.drawRect(0,0, self.grad.width() , self.grad.height() ) paint.end() self.update() self.oya.maskchaged() else: self.setPixmap(self.pix) self.show() class beziercurve(QWidget): def __init__(self,p): super().__init__(p) # self.setMouseTracking (True) # <----------------***** self.oya=None self.plist=[] self.reset() # initialize self.a = QPointF(5,5) self.pselected = 0 # selectedpoint def initial(self , oya): self.oya=oya def reset(self): self.plist.clear() self.plist.append( QPointF(0,270) ) # start point self.plist.append( QPointF(50,220) ) #1 self.plist.append( QPointF(205,65) ) #2 self.plist.append( QPointF(255,15) ) #3 end if self.oya : self.makeLUT() self.update() def mouseMoveEvent(self ,e): if self.pselected==5 : # both points self.plist[1] = e.pos() self.plist[2] = e.pos() self.update() elif self.pselected : self.plist[self.pselected] = e.pos() self.update() self.makeLUT() def mousePressEvent(self ,e): if e.button() == Qt.RightButton : self.pselected=5 return elif e.button() == Qt.MiddleButton : # reset self.reset() return rc = QRectF ( e.pos() - self.a , e.pos() + self.a ) for i, l in enumerate( self.plist ) : if rc.contains( l ) : self.pselected = i if self.pselected != -1 : self.pmove = True def mouseReleaseEvent(self ,e): if self.pselected : # Dorag ing # self.pmove = False self.pselected = 0 # self.makeLUT() self.update() def makeLUT(self): p1=(self.plist[0].x(),self.plist[0].y()) p2=(self.plist[1].x(),self.plist[1].y()) p3=(self.plist[2].x(),self.plist[2].y()) p4=(self.plist[3].x(),self.plist[3].y()) last_x=-1 tt=1/255 # first step ----------------- temp=[] for l in range(256): t=l*tt x = (1-t)**3*p1[0] + 3*(1-t)**2*t*p2[0] + 3*(1-t)*t**2*p3[0] + t**3*p4[0] if round(x)==last_x: continue y = (1-t)**3*p1[1] + 3*(1-t)**2*t*p2[1] + 3*(1-t)*t**2*p3[1] + t**3*p4[1] last_x=round(x) temp.append([ last_x , round(y)]) # second step ----------------- LUT=[] ii=0 # index of temp ( made by first step ) for i in range(256) : if temp[ii][0] == i : LUT.append( temp[ii][1] ) # set y-value ii += 1 else: LUT.append( round((temp[ii][1] + temp[ii-1][1])/2) ) # set y-value self.oya.bezierOp(LUT) def paintEvent (self,e): path = QPainterPath( self.plist[0] ) path.cubicTo( self.plist[1] , self.plist[2] , self.plist[3] ) painter= QPainter() painter.begin(self) painter.setBrush( Qt.white ) painter.drawRect( QRectF( QPointF(0,15),QPointF(256,271) )) painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) ) # painter.setBrush( Qt.blue ) painter.drawPath(path) # draw Base Line painter.setPen( QPen( Qt.blue , 2, style=Qt.DotLine ) ) painter.drawLine( self.plist[0] , self.plist[3] ) # sub # red-sub line painter.setPen( QPen( Qt.red , 2, style=Qt.DotLine ) ) painter.drawLine( self.plist[0] , self.plist[1] ) # sub painter.drawLine( self.plist[2] , self.plist[3] ) # sub # if self.pselected != -1: painter.drawRect( QRectF(QPointF(self.plist[1]-self.a) , QPointF(self.plist[1]+self.a) )) painter.drawRect( QRectF(QPointF(self.plist[2]-self.a) , QPointF(self.plist[2]+self.a) )) painter.end() class Lebel(QWidget): def __init__(self,p): super().__init__(p) self.oya=None self.his=[] def setimage(self,p): his=p.histogram() # print( '平均', sum(his) / len(his)) # print('最大',max(his)) ratio=round((sum(his) / len(his)) /60) # print('レシオ',ratio) self.his.clear() self.his=[ -(round(i/ ratio)-180) for i in his] # <--------------------- self.update() def paintEvent (self,e): painter= QPainter() painter.begin(self) painter.setBrush( Qt.white ) painter.drawRect( QRectF( QPointF(0,0),QPointF(256,180) )) if not len(self.his): painter.end() return painter.setPen( QPen( Qt.red , 2 ) ) for i in range(255): # painter.drawPoint( QPointF( i , self.his[i] )) if self.his[i] >= 180 or self.his[i+1] >= 180: continue painter.drawLine( QPointF( i , self.his[i] ),QPointF( i+1 , self.his[i+1] ) ) painter.setPen( QPen( Qt.green , 1 ) ) for i in range(256,511): if self.his[i] >= 180 or self.his[i+1] >= 180: continue painter.drawLine( QPointF( i-256 , self.his[i] ),QPointF( i-255 , self.his[i+1] ) ) painter.setPen( QPen( Qt.blue , 1 ) ) # count=0 for i in range(512,767) : if self.his[i] >= 180 or self.his[i+1] >= 180: continue painter.drawLine( QPointF( i-512 , self.his[i] ),QPointF( i-511 , self.his[i+1] ) ) # count+=1 # print(count) # print( [ l for i,l in enumerate(self.his) if i> 511] ) painter.end() ``` # enhancer_ui.py ``` # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'pil_ui.ui' # # Created by: PyQt5 UI code generator 5.12 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(964, 674) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setGeometry(QtCore.QRect(340, 0, 340, 440)) self.label.setAcceptDrops(True) self.label.setFrameShape(QtWidgets.QFrame.StyledPanel) self.label.setFrameShadow(QtWidgets.QFrame.Plain) self.label.setObjectName("label") self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_2.setGeometry(QtCore.QRect(600, 560, 75, 23)) self.pushButton_2.setObjectName("pushButton_2") self.radioButton = QtWidgets.QRadioButton(self.centralwidget) self.radioButton.setGeometry(QtCore.QRect(580, 450, 86, 17)) self.radioButton.setChecked(False) self.radioButton.setAutoRepeat(True) self.radioButton.setAutoExclusive(False) self.radioButton.setObjectName("radioButton") self.label_5 = gradlabel(self.centralwidget) self.label_5.setGeometry(QtCore.QRect(0, 0, 341, 441)) self.label_5.setFrameShape(QtWidgets.QFrame.StyledPanel) self.label_5.setObjectName("label_5") self.radioButton_2 = QtWidgets.QRadioButton(self.centralwidget) self.radioButton_2.setGeometry(QtCore.QRect(290, 450, 86, 17)) self.radioButton_2.setAutoRepeat(True) self.radioButton_2.setAutoExclusive(False) self.radioButton_2.setObjectName("radioButton_2") self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.pushButton.setGeometry(QtCore.QRect(850, 330, 101, 111)) self.pushButton.setObjectName("pushButton") self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setGeometry(QtCore.QRect(700, 560, 29, 16)) self.label_2.setObjectName("label_2") self.horizontalSlider = QtWidgets.QSlider(self.centralwidget) self.horizontalSlider.setGeometry(QtCore.QRect(740, 560, 171, 16)) self.horizontalSlider.setMaximum(200) self.horizontalSlider.setProperty("value", 100) self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider.setObjectName("horizontalSlider") self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setGeometry(QtCore.QRect(700, 490, 24, 16)) self.label_3.setObjectName("label_3") self.horizontalSlider_2 = QtWidgets.QSlider(self.centralwidget) self.horizontalSlider_2.setGeometry(QtCore.QRect(740, 490, 171, 16)) self.horizontalSlider_2.setMaximum(200) self.horizontalSlider_2.setProperty("value", 100) self.horizontalSlider_2.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider_2.setObjectName("horizontalSlider_2") self.label_7 = QtWidgets.QLabel(self.centralwidget) self.label_7.setGeometry(QtCore.QRect(690, 580, 50, 16)) self.label_7.setObjectName("label_7") self.horizontalSlider_5 = QtWidgets.QSlider(self.centralwidget) self.horizontalSlider_5.setGeometry(QtCore.QRect(740, 580, 171, 16)) self.horizontalSlider_5.setMaximum(300) self.horizontalSlider_5.setProperty("value", 100) self.horizontalSlider_5.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider_5.setObjectName("horizontalSlider_5") self.spinBox = QtWidgets.QSpinBox(self.centralwidget) self.spinBox.setGeometry(QtCore.QRect(920, 490, 42, 22)) self.spinBox.setMaximum(200) self.spinBox.setProperty("value", 100) self.spinBox.setObjectName("spinBox") self.horizontalSlider_8 = QtWidgets.QSlider(self.centralwidget) self.horizontalSlider_8.setGeometry(QtCore.QRect(50, 450, 179, 16)) self.horizontalSlider_8.setOrientation(QtCore.Qt.Horizontal) self.horizontalSlider_8.setObjectName("horizontalSlider_8") self.spinBox_4 = QtWidgets.QSpinBox(self.centralwidget) self.spinBox_4.setGeometry(QtCore.QRect(240, 440, 40, 22)) self.spinBox_4.setObjectName("spinBox_4") self.label_10 = QtWidgets.QLabel(self.centralwidget) self.label_10.setGeometry(QtCore.QRect(10, 450, 31, 16)) font = QtGui.QFont() font.setBold(True) font.setWeight(75) self.label_10.setFont(font) self.label_10.setLayoutDirection(QtCore.Qt.LeftToRight) self.label_10.setAlignment(QtCore.Qt.AlignCenter) self.label_10.setObjectName("label_10") self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_3.setGeometry(QtCore.QRect(600, 590, 75, 23)) self.pushButton_3.setObjectName("pushButton_3") self.bezier = beziercurve(self.centralwidget) self.bezier.setGeometry(QtCore.QRect(690, 0, 275, 280)) self.bezier.setMinimumSize(QtCore.QSize(0, 0)) self.bezier.setObjectName("bezier") self.radioButton_5 = QtWidgets.QRadioButton(self.centralwidget) self.radioButton_5.setGeometry(QtCore.QRect(740, 290, 41, 17)) self.radioButton_5.setChecked(True) self.radioButton_5.setAutoRepeat(True) self.radioButton_5.setAutoExclusive(False) self.radioButton_5.setObjectName("radioButton_5") self.radioButton_6 = QtWidgets.QRadioButton(self.centralwidget) self.radioButton_6.setGeometry(QtCore.QRect(790, 290, 41, 17)) self.radioButton_6.setChecked(True) self.radioButton_6.setAutoRepeat(True) self.radioButton_6.setAutoExclusive(False) self.radioButton_6.setObjectName("radioButton_6") self.radioButton_7 = QtWidgets.QRadioButton(self.centralwidget) self.radioButton_7.setGeometry(QtCore.QRect(840, 290, 41, 17)) self.radioButton_7.setChecked(True) self.radioButton_7.setAutoRepeat(True) self.radioButton_7.setAutoExclusive(False) self.radioButton_7.setObjectName("radioButton_7") self.widget = Lebel(self.centralwidget) self.widget.setGeometry(QtCore.QRect(10, 470, 270, 180)) self.widget.setObjectName("widget") self.widget_2 = Lebel(self.centralwidget) self.widget_2.setGeometry(QtCore.QRect(310, 470, 270, 180)) self.widget_2.setObjectName("widget_2") self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_4.setGeometry(QtCore.QRect(710, 330, 75, 41)) self.pushButton_4.setObjectName("pushButton_4") self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_5.setGeometry(QtCore.QRect(710, 410, 75, 31)) self.pushButton_5.setObjectName("pushButton_5") self.pushButton_6 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_6.setGeometry(QtCore.QRect(710, 380, 75, 23)) self.pushButton_6.setObjectName("pushButton_6") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 964, 17)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.pushButton.clicked.connect(MainWindow.bye) self.horizontalSlider.valueChanged['int'].connect(MainWindow.slider_1) self.horizontalSlider_2.valueChanged['int'].connect(MainWindow.slider_2) self.pushButton_2.clicked.connect(MainWindow.test) self.radioButton.toggled['bool'].connect(MainWindow.radiochecked) self.horizontalSlider_2.valueChanged['int'].connect(self.spinBox.setValue) self.horizontalSlider_5.valueChanged['int'].connect(MainWindow.slider_5) self.radioButton_2.toggled['bool'].connect(MainWindow.radiotoggle1) self.pushButton_3.clicked.connect(MainWindow.GetColor) self.pushButton_4.clicked.connect(MainWindow.imagefix) self.pushButton_5.clicked.connect(MainWindow.imagesave) self.pushButton_6.clicked.connect(MainWindow.imagerestore) self.spinBox.valueChanged['int'].connect(self.horizontalSlider_2.setValue) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", "TextLabel")) self.pushButton_2.setText(_translate("MainWindow", "test")) self.radioButton.setText(_translate("MainWindow", "MASK")) self.label_5.setText(_translate("MainWindow", "TextLabel")) self.radioButton_2.setText(_translate("MainWindow", "MASK")) self.pushButton.setText(_translate("MainWindow", "BYE")) self.label_2.setText(_translate("MainWindow", "明るさ")) self.label_3.setText(_translate("MainWindow", "回転")) self.label_7.setText(_translate("MainWindow", "シャープネス")) self.label_10.setText(_translate("MainWindow", "R")) self.pushButton_3.setText(_translate("MainWindow", "Color")) self.radioButton_5.setText(_translate("MainWindow", "r")) self.radioButton_6.setText(_translate("MainWindow", "g")) self.radioButton_7.setText(_translate("MainWindow", "b")) self.pushButton_4.setText(_translate("MainWindow", "FIX")) self.pushButton_5.setText(_translate("MainWindow", "SAVE")) self.pushButton_6.setText(_translate("MainWindow", "Restore")) from enhancer_subclass import Lebel, beziercurve, gradlabel if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_()) ``` --- --> [PyQt で画像のビューアを作る もくじ](https://mimemo.io/m/QORbW4qkvOoda0N)