「Krita」と「Python」のクラスで画像を加工しよう
はじめに
今回は、これまでとちょっと違い「クラス(class)」を使ってKritaでお絵描きをします。と言っても第3回から出てきた「QColor」や「QPixmap」などもクラスで、自分でスクリプティングする部分でクラスを作るということです。
また「ファイルを開く」ダイアログを使った応用例として、開いた画像をピクセル単位で加工します。1つ目のサンプルスクリプトは開いた画像に市松模様(いわゆるチェック柄)を上書きします。2つ目のサンプルスクリプトは開いた画像のピクセルをランダムに移動して擦りガラスのような加工をします。
本連載でこれまでやってきたことを応用すれば簡単にプログラミングできます。今回の2つのサンプルでは、これまでに加えて、画像を読み込み、1ピクセルずつの色を取得して1ピクセルずつ描いていきます。また、関数をオリジナルのクラスやメソッドに書き換えたり、今までの変数をクラスのプロパティに書き換えたりします。
画像の加工
第7回ではファイルを開くダイアログでファイル名を取得しただけでしたが、今回は取得したファイル名の画像ファイルを開いて加工します。と言っても、画像ファイルを開くには「QImage」クラスの「load」メソッドの引数に画像ファイルのパスを指定するだけです。
画像を加工するには、ピクセル単位で色を変更していきます。ベクトル図形の場合は頂点データがあるのでそれを変形しますが、塗り図形の場合は色のピクセルデータがあるだけなので、色を操作します。
市松模様の加工をする
市松模様は「QImage」クラスの「setPixelColor」メソッドで黒い点を描くだけです。X座標もY座標も偶数のときと、X座標もY座標も奇数のときに黒い点を描けば市松模様になります(図1)。
もっと使えるようにするには、例えば市松模様を新たにレイヤーを作って被せると良いでしょう。市松模様のピクセルサイズや色を変えてみても良いです。
・サンプルスクリプト「hatching.py」# モジュール from PyQt5.Qt import * # クラス class Hatching(): # ドキュメントの作成 def create_doc(self,width,height): self.doc = Krita.instance().createDocument(width, height, "Document name", "RGBA", "U8", "", 300.0) Krita.instance().activeWindow().addView(self.doc) # 画像ファイルを開く def open_img(self): pixelBytes = QByteArray() self.img = QImage(pixelBytes,0,0,QImage.Format_ARGB32) filter = str('Supported Files (*.jpg *.png);;All files (*)') fileName = QFileDialog.getOpenFileName(None,str(''),str(''),filter) self.img.load(fileName[0]) # 市松模様の描画 def draw_img(self): for x in range(0,self.img.width()): for y in range(0,self.img.height()): if (((y%2) + x) % 2) == 0: self.img.setPixelColor(x,y,QColor(0,0,0,255)) # レイヤーのセット def set_layer(self): root = self.doc.rootNode() layer = root.childNodes()[0] if self.img.sizeInBytes() == 4 * layer.channels()[0].channelSize() * self.doc.width() * self.doc.height(): ptr = self.img.bits() ptr.setsize(self.img.byteCount()) layer.setPixelData(QByteArray(ptr.asstring()), 0, 0, self.doc.width() , self.doc.height()) else: print('Error') # メイン処理 def create(self): self.open_img() self.create_doc(self.img.width(),self.img.height()) self.draw_img() self.set_layer() self.doc.refreshProjection() # インスタンスの生成 ins = Hatching() ins.create()
【サンプルスクリプトの解説】
関数を「create_doc」メソッドと「set_layer」メソッドに書き換え、変数をプロパティに書き換えます。
「open_img」メソッドで「QFileDialog」クラスの「getOpenFileName」スタティックメソッドを使って画像ファイルを指定し、「QImage」クラスの「load」メソッドで画像を開きます。
「draw_img」メソッドで(x,y)が市松模様になる座標だけ黒色のピクセルを塗ります。
「create」メソッドでopen_imgメソッドを呼び出し、開いた画像のサイズでドキュメントを作ってdraw_imgメソッドとset_layerメソッドを呼び出した後、リフレッシュします。
擦りガラスの加工をする
擦りガラス風の加工(図2)をするには、ピクセルをランダムに移動すれば良いだけです。今回はピクセルの(x,y)座標を-5以上5以下だけランダムに移動します。「create_doc」メソッド、「open_img」メソッド、「set_layer」メソッド、「create」メソッドについては、サンプルスクリプト「hatching.py」と全く同じです。
・サンプルスクリプト「effect.py」# モジュール from PyQt5.Qt import * import random # クラス class Effect(): (中略) # ピクセル毎の移動 def draw_img(self): delta = 5 for i in range(0,1): for x in range(delta,self.img.width()-delta): for y in range(delta,self.img.height()-delta): xx = x + random.randint(-delta,delta) yy = y + random.randint(-delta,delta) color = self.img.pixelColor(xx,yy) self.img.setPixelColor(x,y,color) (中略) # インスタンスの生成 ins = Effect() ins.create()
【サンプルスクリプトの解説】
乱数を扱う「random」モジュールを呼び出します。
今回の2つのサンプルのように初期化の「__init__」メソッドは省略できます。
「draw_img」メソッドで、ピクセル毎に-delta≦(x,y)≦deltaだけランダムにピクセルをコピーして描画します。
おわりに
今回はクラスを使った実例として、「ファイルを開く」ダイアログで開いた画像に市松模様を施したり、ピクセルごとにランダムに移動して擦りガラス風の加工をする方法を解説しました。