--- Title: PyQt で画像のビューアを作る 7 Author: yamasyuh68 Web: https://mimemo.io/m/QORbW4qkJ6oda0N --- - pillow の明るさが嫌なのでトーンカーブのことを調べだした ---- 190702 # ベジェ曲線をグリグリしてみた - マスクでenhanceは出来たんだけど肝心のpillowのenhanceが不満で、いろいろ調べてるうちにトーンカーブでピクセルを補正していくしかないような気がしてきた - で、まずはマウスでグリグリ曲線を描くにはどうしたら良いのだろうと思い、Qtでベジェを書く実験をしたら結構簡単にできた でもこの曲線を数学の関数として扱って、与えられたxに対してYを返さなければいけないがやり方がわかんない┐('д')┌ 調べればわかるんだろうけど、そもそもベジェを書くことが目的では無いことに気づいた( ´∀`) まあ楽しかったしこれでパスオブジェクトを作れるようになったのでどこかでまたやろう - トーンカーブの実装には多分別のアプローチが必要 右上がりの直線を書いて、そこにポイントを追加してなめらかな曲線にする、これは多分スプライン補間って分野だ python ってそういう数学的なものは得意らしい scipy ってのを早速インストしてみたので調べてやってみよう ---- 190704 # トーンカーブの実装 - 右上がりの直線を描いてマウスで上に持ち上げたら線が上に膨らんで画像も明るくなるって感じ トーンカーブですね、とりあえず出来た(・∀・)ノ ``` import sys from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtCore import QPointF , QPoint ,QRectF ,Qt from PyQt5.QtGui import QPainterPath ,QPainter ,QPen from scipy import interpolate from PIL import Image import numpy class App(QWidget): def __init__(self): super().__init__() self.setWindowTitle('hogetta') self.setGeometry( 500, 50 , 500 , 500 ) self.x=[0,170,255] # to use scipy function self.y=[255,170,0] self.f_list=[] # look up table ( curve ) for l in range(256): self.f_list.append(QPoint( l , 255-l)) # make initial table --> line self.a = QPoint(4,4) # Tolerance in detecting point self.pselected = -1 # selectedpoint self.pmove = 0 # move flag self.show() def mouseMoveEvent(self ,e): if self.pmove : # Dorag ing self.x[1] = e.pos().x() self.y[1] = e.pos().y() self.makeLUT() self.update() def mousePressEvent(self ,e): self.pmove = True def mouseReleaseEvent(self ,e): self.pmove = False self.showimg() def makeLUT(self): # make look up table by function # linear quadratic × cubic func = interpolate.interp1d(self.x, self.y, kind="quadratic" ) for l in range(256): value=int(func(l)) value = value if value >=0 else 0 self.f_list[l] = QPoint( l , value ) def paintEvent (self,e): painter= QPainter() painter.begin(self) painter.setPen( QPen( Qt.black , 2, style=Qt.SolidLine ) ) for l in range( 255 ): painter.drawPoint( self.f_list[l] ) painter.setPen( QPen( Qt.red , 8, style=Qt.SolidLine ) ) painter.drawPoint( self.x[1] , self.y[1] ) painter.end() def showimg(self): im = Image.open() rgb_im = im.convert('RGB') size = rgb_im.size im2 = Image.new('RGBA',size) data = im.getdata() data2=[] width, height = im.size for l in range( width * height ): r,g,b = data[l] r = 255-self.f_list[r].y() # <<=== g = 255-self.f_list[g].y() # <<=== b = 255-self.f_list[b].y() # <<=== data2.append((r,g,b,0)) # <======== tupple!!!!!! im2.putdata(data2) im2.show() return # getpixel 遅い for x in range(size[0]): for y in range(size[1]): r,g,b = rgb_im.getpixel((x,y)) # print(r,g,b) r = int(self.f_list[r].y()) g = int(self.f_list[g].y()) b = int(self.f_list[b].y()) im2.putpixel((x,y),(r,g,b,0)) im2.show() if __name__ == "__main__": app = QApplication(sys.argv) ex = App() sys.exit(app.exec_()) ``` - 上下がおかしいかも( ̄― ̄?) 描画ってY軸が逆になるからやりづらいのよ ## スプライン補間した関数を取得 - scipy君に頼んで何とか出来たが、よくみる interpolate.interp1d(x, y, kind="cubic" ) これだとエラーになる cubic の場合、与える**xがある程度均等に並んでる**必要があるみたいだ トーンカーブは始点と終点は固定で、中間の任意の点を上げ下げする こうした三点を与えても計算出来ないんだと思う - でドキュメントを読んで kind パラメータを順に試したら、 interpolate.interp1d(x, y, kind=**"quadratic"** ) これでイケました 試行錯誤なので理屈はわかってませんが( ´∀`) - この関数さえ出来ればLUTを作るのは単純作業で簡単 - LUTを元にドットを描画するように曲線を描くことに成功 ## 画像に適用 - で、LUTを元にpillowでピクセルを補正して表示することに成功 - これは簡単だったんだけど、getpixcelだと遅くて使えない┐('д')┌ - せっかくscipyをインストしたのでそれについてきたNumpyでやってみたが、arrayを一括で上げようとしてもうまくいかない このサイト ↓ の情報だと、Numpyよりもpillowのgetdataが速いって事だったので試したら何とかイケた https://kuranabe.hatenablog.com/entry/2018/08/18/234355 - getdata はピクセルデータを縦*横の数の一列のリストで取得できる 単純に全てのデータをLUTによって変換して新しいデータを作って、もとのpillowにputdata()で戻してやればOK ちなみにこのデータはリストだけどそれぞれの要素はタプルで返すことに注意