wxPythonのwx.ImageとPython Imaging Libraryの変換方法

wxPythonで用意されているImageやBitmapをPython Imaging Libraryで使うにはどうすればいいのか?という話。
wxPythonはさまざまなフォーマットの画像を読み込めるので便利です。wx.ImageとPILを相互変換できるなら、
読み込んだwxImageをPython Imaging LibraryのImageに変換して、画像処理に使うことができます。
相互変換するための関数の例と、実際に作成したサンプルコードを紹介します。

wx.ImageからPILへの変換

newメソッドでpilの画像を入力画像と同サイズで作ります。wxImageのGetDataメソッドで取り出したRawデータを、pilのfromstringメソッドで取り込めばOKです。

#convert wx.Image to PIL Image
def wxImageToPil(wximage):
    pil = Image.new('RGB', (wximage.GetWidth(), wximage.GetHeight()))
    pil.fromstring(wximage.GetData())
    return pil

PILからwx.Imageへの変換

今度は逆にwxImageをEmptyImageメソッドで新たに作成します。
pilのtostringメソッドでRawデータを取り出したあと、wxImageのSetDataメソッドで書き込めばOKです。

#convert PIL Image to wx.Image
def pilToImage(pil):
    wximage = wx.EmptyImage(pil.size[0], pil.size[1])
    wximage.SetData(pil.convert('RGB').tostring())
    return wximage

この二つの関数を使って画像処理アプリのひな形を作成してみます。
以前作ったImage Viewerのコードを使います。↓
wxPythonのwxImageで画像を表示する - white wheelsのメモ

画像の読み込みとPILへの変換

適当な画像sample.bmpを用意して、wxImageに読み込みます。ウインドウに表示するためにBitmapに変換し、さらに画像データを扱うためにPilに変換します。このときPILはframeのメンバとして持たせておきます。

        #open and setup wx.Image
        wximage = wx.Image('sample.bmp')
        self.bitmap = wximage.ConvertToBitmap() #wxImage to Bitmap
        self.pilImage = wxImageToPil(wximage) #wxImage to pil

画像処理部分

メンバとして保持しているPILの画像に処理を行ったあと、wxImageにもどして、さらにBitmapに変換すると良いです。
グレースケールへの変換が下の例。

        #------write codes for image processing here---#
        pil = self.pilImage.convert("L")
        #----------------------------------------------#
        wximage = pilToImage(pil) #pil to wxImage
        self.bitmap = wximage.ConvertToBitmap() #wxImage to Bitmap

以上でPILを使って画像処理を行うGUIアプリの土台が整いました。
左クリックでグレースケールに、右クリックで元の画像にするサンプルが下の例です。

import wx #wx.Image
import Image #PIL

#convert wx.Image to PIL Image
def wxImageToPil(wximage):
    pil = Image.new('RGB', (wximage.GetWidth(), wximage.GetHeight()))
    pil.fromstring(wximage.GetData())
    return pil
#convert PIL Image to wx.Image
def pilToImage(pil):
    wximage = wx.EmptyImage(pil.size[0], pil.size[1])
    wximage.SetData(pil.convert('RGB').tostring())
    return wximage

class myFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title)
        #event handling
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)        
        #open and setup wx.Image
        wximage = wx.Image('sample.bmp')
        self.bitmap = wximage.ConvertToBitmap() #wxImage to Bitmap
        self.pilImage = wxImageToPil(wximage) #wxImage to pil
                
        self.SetSize(wximage.GetSize())
    def OnPaint(self, event=None):
        deviceContext = wx.PaintDC(self)
        deviceContext.Clear()
        deviceContext.SetPen(wx.Pen(wx.BLACK, 4))
        deviceContext.DrawBitmap(self.bitmap, 0, 0, True)

    def OnMouseLeftDown(self, event):
        #------write codes for image processing here---#
        pil = self.pilImage.convert("L")
        #----------------------------------------------#
        wximage = pilToImage(pil) #pil to wxImage
        self.bitmap = wximage.ConvertToBitmap() #wxImage to Bitmap
        self.Refresh()
    def OnMouseRightDown(self, event):
        wximage = pilToImage(self.pilImage) #pil to wxImage
        self.bitmap = wximage.ConvertToBitmap() #wxImage to Bitmap
        self.Refresh()
        
app = wx.App(False)
frame = myFrame(None, "Image Converter")
frame.Show()
app.MainLoop()