Pixel iterator(matrix 版) - Rubyist の Python 学習記録(10)

PythonLibrary Python NumPy 2019年 1月 10日

ベクトル版の Pixel iterator が作成できたので,これを行列に拡張する.

ピクセルベースのイテレータのテスト(matrix 版)

  • tests/test_mono_image.py に書いた generate_with_int_hist のテストに行列の追記をする. vec の部分を mat に変えただけで,呼び出し方などは全く変わっていない. 変更点は MonoImage の作成時に create_with_array で行列を渡しているだけである. 今回は二回目の呼び出しチェックも最初から記述している.
  • with such.A('MonoImage class') as it:
        (省略)
        @it.should('generate integer histogram')
        def test():
            correct = MonoImage.create_with_array([0, 2, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], np.uint32, 32);
    
            ### vector test
            vec = MonoImage.create_with_array([3, 1, 4, 1, 5, 9], np.uint8, 4);
            it.assertEqual(vec.generate_with_int_hist(), correct)
            # Check if the method is called twice
            it.assertEqual(vec.generate_with_int_hist(), correct)
    
            ### matrix test
            mat = MonoImage.create_with_array([[3, 1, 4], [1, 5, 9]], np.uint8, 4);
            it.assertEqual(mat.generate_with_int_hist(), correct)
            # Check if the method is called twice
            it.assertEqual(mat.generate_with_int_hist(), correct)

MonoImagePixelIterator のコンストラクタの修正

  • 前回作成したコンストラクタで __iter__ で実行する generator function を _f に代入していた. 行列に対応するには,この部分で ndarray の次元を確認し,vector_iterator_func か matrix_iterator_func かを切り替えればよい. また,_getter と _setter も同様にベクトルと行列で切り替える. ついでに ndim も今後使いそうなので, Computed property に追加しておいた.
  • class MonoImagePixelIterator:
        def __init__(self, mono_image):
            self._mono_image = mono_image
            self._width = mono_image.width
            self._height = mono_image.height
            self._f = self.vector_iterator_func if mono_image.ndim == 1 else self.matrix_iterator_func
            self._getter = self.vector_getter_func if mono_image.ndim == 1 else self.matrix_getter_func
            self._setter = self.vector_setter_func if mono_image.ndim == 1 else self.matrix_setter_func

matrix_iterator_func の実装

  • vector_iterator_func と同様に matrix_iterator_func を作成した. self._nowr, self._nowc でスキャンすること以外は vector_iterator_func と同様である.
  • class MonoImagePixelIterator:
        (中略)
        def matrix_iterator_func(self):
            self._nowr = 0
            self._nowc = 0
            while (self._nowr != -1):
                yield self
                self._nowc += 1
                if self._nowc == self._width:
                    self._nowc = 0
                    self._nowr += 1
                    if self._nowr == self._height:
                        self._nowr = -1
  • matrix_getter_func は _nowr, _nowc の位置の値を単に返す.
  • def matrix_getter_func(self):
        return self._mono_image._array[self._nowr, self._nowc]
  • matrix_setter_func は _nowr, _nowc の位置のスライスに指定された値を書き込む.
  • def matrix_setter_func(self, v):
        self._mono_image._array[self._nowr, self._nowc] = v

Rubyist なため,効率よりもどうしたら DRY (Don't repeat yourself) になるかをつい考えてしまう. vector 版でかなり DRY になるよう努力したため,matrix 版は非常に少ない追加記述だけですんだ. これも NumPy の柔軟さのおかげである.

あまり長くならなかったが今日はここまで.