Python+OpenCVで、イメージの画素単位での操作方法をまとめました。実質的にはNumPyでの行列操作方法の説明です。
「Python+OpenCV」で、画素単位での画像操作をするケースはそう多くありません。 でも、「まったくやり方がわからない」ってのも困りますので、調査結果を書いておきます。
結果かなりパワフルで、C++からcv::Matを直接使うより便利な点もあるってことがわかりました。
ただし、画素単位の参照、書き換えが非常に遅いため、速度に関してはC++の圧勝です。
ここで宣伝(笑):以下のリンクは、OpenCV + C++でフィルター構成をXMLに記述可能なフィルター処理ライブラリをご紹介しております。
OpenCVによる画像処理入門 (KS情報科学専門書)
posted with amazlet at 15.06.13
※ 以下、コードは冗長に書いています。実行可能なソースの例は一番下にあります。
ブランクイメージの生成
初期状態は全面黒のようですね
# 640 x 480 CV_8UC3 イメージの生成 cols = 640 rows = 480 image = np.zeros((rows, cols, 3), np.uint8)
※ 以降のコードのimageは全部これ↑です。
画素の更新(その1:itemsetを使用してチャネル別)
# 座標(適当です) row = 48 col = 64 # 画素値(適当です) b = 0 g = 128 r = 255 # チャネル別 image.itemset((row, col, 0), b) image.itemset((row, col, 1), g) image.itemset((row, col, 2), r)
全チャネルまとめて更新するのはムリなんかな? 次のやり方では出来ます。
画素の更新(その2:配列的アクセス)
# 座標(適当です) row = 48 col = 64 # 画素値(適当です) b = 0 g = 128 r = 255 # 3チャネルまとめて設定 image[480 - row, 640 - col] = [b, g, r] # チャネル別 image[480 - row, 640 - col, 0] = b image[480 - row, 640 - col, 1] = g image[480 - row, 640 - col, 2] = r
画素の更新(拡張編:範囲とステップを指定して塗りつぶしなど)
# イメージ全体を暗い青で塗りつぶす image[:,:] = [128, 0, 0] # (120,80)-(240,160)をオレンジで塗りつぶす image[120:240,80:160] = [0, 255, 128] # (240,80)-(360,160)を黄緑で4画素飛ばしで塗りつぶす image[240:360:4, 80:160:4] = [128, 255, 128]
これはかなりオドロイタ。演算子がオーバーロードされてるんですかね。
具体例は以下に書いてます。
おまけ
塗りつぶしの機能を使って単純なパターンを描いてみた。
こんなのが表示されます↓
# vim: set fileencoding=utf-8 : import numpy as np import cv2 cols = 640 rows = 480 #イメージ生成 image = np.zeros((rows, cols, 3), np.uint8) div = 4 # 縦横の分割数 w = cols / div # 分割された領域の横幅 h = rows / div # 分割された領域の縦幅 for segrow in xrange(div): y1 = segrow * h # 分割領域上 y2 = y1 + h # 分割領域下 c1 = (segrow + 1) * 256 / div - 1 # 色値1 for segcol in xrange(div): x1 = segcol * w #分割領域左 x2 = x1 + w #分割領域右 c2 = (segcol + 1) * 256 / div - 1 # 色値2 s = (segrow + segcol) * 4 + 2 # ステップ #(x1,y1)-(x2,y2)の矩形を塗りつぶす image[y1:y2, x1:x2] = [c1/2, c2/2, 0] m = (segrow*div+segcol) % 4 if m == 0: #等間隔に別色で点を打つ image[y1:y2:s,x1:x2:s] = [(c1+c2)/2, c1, c2] elif m == 1: # 等間隔の水平線 image[y1:y2:s,x1:x2] = [(c1+c2)/2, c1, c2] elif m == 2: # 等間隔の垂直線 image[y1:y2,x1:x2:s] = [(c1+c2)/2, c1, c2] elif m == 3: # 等間隔の斜め線 for i in xrange(s): image[y1+i:y2+i:s,x1+i:x2+i:s] = [(c1+c2)/2, c1, c2] # 表示して[ESC]が押されるまで待つ cv2.imshow("image", image) while cv2.waitKey(33) != 27: pass