pythonでundo機能を実装してみた。


はじめに

undo・・・直前の操作を取り消すこと (パワポのctr+Z)
redo・・・undoで一旦取り消した操作をやり直すこと(パワポのctr+Shift+Z)

画像処理とする際undoとredoがあると便利だなと思い、
自分なりにpythonで実装してみました。

undo redo 実装の説明

undo, redo用のデータを保存するリストをundo_list, redo_listとします。

画像処理をする際に、処理前のデータを都度undo_listに追加することで,後から復元できるようにしていきます。

undo操作の際は、以下の操作を行います
・現在の画像をredo_listに追加する。
・undo_listの末尾のデータを現在の画像と置き換える。

redo操作の際はundo操作の逆を行います。
・現在の画像をundo_listに追加する。
・redo_listの画像を、現在の画像と置き換える。

実際の実装

以上のことを踏まえて、以下のように実装してみました。

class Image(object):
    def __init__(self,data):
        self.data=data
        self.shape=data.shape
        self.undos=[] #undo_list
        self.redos=[] #redo_list

    def __set_undo(self):
        self.undos.append(self.data.copy()) #undo_listの末尾に今のデータを追加    
        if len(self.undos)>5: 
            self.undos.pop(0) #undo listの先頭の値を削除する。
            #undo_listにデータが蓄積されることを防ぐために、
            #5つ以上の数のデータが追加された場合は、リストの先頭のデータを削除。

    def __set_redo(self):
        self.redos.append(self.data.copy()) #redo_listの末尾に今のデータを追加 
        if len(self.undos)>5:
            self.redos.pop(0) #redo listの先頭の値を削除する。

    def undo(self):
        if not self.undos: #undo listが空の場合         
            print('ERROR: No undo data')
            sys.exit() 
        self.__set_redo()
        self.data=self.undos.pop(-1) #undo listから一番最後の値を取得し、削除する。

    def redo(self):
        if not self.redos: #redo listが空の場合  
            print('ERROR: No redo data')
            sys.exit() 
        self.__set_undo()
        self.data=self.redos.pop(-1) #redo listから一番最後の値を取得し、削除する。

    #適当な処理
    def add_noise(self,var=30):
        self.__set_undo()
        noise=np.random.normal(scale=var, size=self.shape)
        self.data+=noise

    def rot(self, k):
        self.__set_undo()
        self.data=np.rot90(self.data, k=k)

    def plot(self):
        plt.figure()
        plt.imshow(self.data,  cmap='gray')


if __name__=='__main__':
    data=misc.ascent().astype(np.float32) # 階段の画像の取得
    img=Image(data)        

適当な処理として、ガウシアンノイズを加える操作と、回転操作の関数を加えました。
各処理の前にself.__set_undo()を行うことで、処理前の画像をundo_listに加えています。

画像処理をしていきます。

    img.plot()     #最初の画像の表示
    img.add_noise()
    img.plot()
    img.rot(k=1)
    img.plot()

ここからundoしていきます。

    img.undo()
    img.plot()
    img.undo()
    img.plot()

ここでもう一回img.undo()をするとundo listが空のためエラーが返されます。

ERROR: No undo data
An exception has occurred, use %tb to see the full traceback.

SystemExit

今度はredoしていきます

    img.redo()
    img.plot()
    img.redo()
    img.plot()

まとめ

自分の勉強がてら、undo機能を作ってみました。
なにかありましたらコメントいただけると幸いです。