銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

OpenCV + Python + NumPy で画像の画素を操作する

PythonOpenCVで、イメージの画素単位での操作方法をまとめました。実質的にはNumPyでの行列操作方法の説明です。

f:id:takamints:20151128104233p:plain

PythonOpenCV」で、画素単位での画像操作をするケースはそう多くありません。 でも、「まったくやり方がわからない」ってのも困りますので、調査結果を書いておきます。

結果かなりパワフルで、C++からcv::Matを直接使うより便利な点もあるってことがわかりました。

ただし、画素単位の参照、書き換えが非常に遅いため、速度に関してはC++の圧勝です。

ここで宣伝(笑):以下のリンクは、OpenCV + C++でフィルター構成をXMLに記述可能なフィルター処理ライブラリをご紹介しております。

takamints.hatenablog.jp


OpenCVによる画像処理入門 (KS情報科学専門書)
小枝 正直 上田 悦子 中村 恭之
講談社
売り上げランキング: 88,924

※ 以下、コードは冗長に書いています。実行可能なソースの例は一番下にあります。

ブランクイメージの生成

初期状態は全面黒のようですね

# 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]

これはかなりオドロイタ。演算子オーバーロードされてるんですかね。

具体例は以下に書いてます。

おまけ

塗りつぶしの機能を使って単純なパターンを描いてみた。

こんなのが表示されます↓

f:id:takamints:20150304221505p:plain

# 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