OpenCV-Python Tutorials (1) ~Core Operations~ 前半

OpenCV-Python Tutorialsをやっているのでメモしながら進めていこうと思います.

3つ目の章Core Operationsから始めます.

公式: Core Operations — OpenCV-Python Tutorials 1 documentation

Core Operations

基本的な操作

目標
  • ピクセル値の利用,修正
  • 画像の属性の利用
  • 画像の一部の操作
  • 画像の切り取り,合成

ピクセル値の利用と修正

最初にカラー画像を読み込む.

In [1]: import cv2

In [2]: import numpy as np

In [3]: img = cv2.imread('messi5.jpg')

これで,行,列の値の組み合わせからピクセル値を取得できる. BGRの画像に対しては,Blue, Green, Red の値の Array を返す.グレースケールの画像に対しては,対応するものを返す.

In [4]: px = img[100,100]

In [5]: print(px)
[157 166 200]

# blue のピクセルだけを取得
In [6]: blue = img[100, 100, 0]

In [7]: print(blue)
157

同様のやり方でピクセル値を修正できる.

In [8]: img[100,100] = [255,255,255]

In [9]: print(img[100,100])
[255 255 255]

より良いやり方は:

# RED の値を取得
In [10]: img.item(10,10,2)

# RED の値を修正
In [11]: img.itemset((10,10,2),100)

In [12]: img.item(10,10,2)
Out[12]: 100

画像の属性の利用

画像の属性は行,列,チャンネルの数,画像データの型,ピクセルの数を含んでいる.

画像の形は img.shape で利用できる.(カラー画像の場合は)行,列,チャンネルの数の Array を返す:

In [13]: print(img.shape)
(2448, 3264, 3)

画像全体のピクセル数は img.size で利用できる:

In [14]: print(img.size)
23970816

画像データの型は img.dtype に含まれている:

In [15]: print(img.dtype)
uint8

画像の一部の操作

Numpyのindexを利用することで画像の一部を操作できる.

In [16]: ball = img[280:340, 330:390]

In [17]: img[273:333, 100:160] = ball

画像の切り取り,合成

画像の B,G,R のチャンネルはそれぞれの独立した面に分けることができる. そして,独立したチャンネルを合わせて戻すことで BGR 画像に戻すことができる.

In [18]: b,g,r = cv2.split(img)

In [19]: img = cv2.merge((b,g,r))

または

In [21]: img[:,:,2] = 0

画像の境界を作る(パディング)

画像の周りの境界を作りたい場合は, cv2.copyMakeBorder() を使うことができる.これは次のような引数を持つ:

  • src - 入力画像
  • top, bottom, left, right - 境界の幅
  • borderType - 以下のFlagでどの種類の境界が足されるかを定義する
    • cv2.BORDER_CONSTANT
    • cv2.BORDER_REFLECT
    • cv2.BORDER_REFLECT_101 or cv2.BORDER_DEFAULT
    • cv2.BORDER_REPLICATE
    • cv2.BORDER_WRAP
  • value - cv2.BORDER_CONSTANTの場合の境界線の色

デモ用のサンプルコードは以下:

In [1]: import cv2
In [2]: import numpy as np
In [3]: from matplotlib import pyplot as plt

In [4]: BLUE = [255,0,0]

In [5]: img1 = cv2.imread('opencv_logo.png')

In [6]: replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)

In [7]: reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)

In [8]: reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)

In [9]: reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)

In [10]: reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)                                                                      

In [11]: wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)

In [12]: constant = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)                                                                                              
In [13]: plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')                                                                                                              
Out[13]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x107ebf780>,
 <matplotlib.image.AxesImage at 0x11316b278>,
 <matplotlib.text.Text at 0x113132c50>)

In [14]: plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')                                                                                                        
Out[14]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x113180898>,
 <matplotlib.image.AxesImage at 0x108830550>,
 <matplotlib.text.Text at 0x11316ba20>)

In [15]: plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')                                                                                                            
Out[15]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x108830be0>,
 <matplotlib.image.AxesImage at 0x108882278>,
 <matplotlib.text.Text at 0x10885e978>)

In [16]: plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')                                                                                                     
Out[16]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x108882898>,
 <matplotlib.image.AxesImage at 0x1088b6f28>,
 <matplotlib.text.Text at 0x108895898>)

In [17]: plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')                                                                                                                  
Out[17]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x1088bf5f8>,
 <matplotlib.image.AxesImage at 0x10898e160>,
 <matplotlib.text.Text at 0x1088e7860>)

In [18]: plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')                                                                                                          
Out[18]: 
(<matplotlib.axes._subplots.AxesSubplot at 0x10898e780>,
 <matplotlib.image.AxesImage at 0x1088d44a8>,
 <matplotlib.text.Text at 0x1089b4470>)

In [19]: plt.show()

画像への算術的操作

目標
  • 足し算,引き算,ビット演算などといった算術的操作を学ぶ
  • cv2.add(), cv2.addWeighted()といったものを学ぶ

画像の足し算

cv2.add() という OpenCV の機能,または numpy の操作 res = img1 + img2 によって2つの画像を足すことができる.

In [1]: import cv2

In [2]: import numpy as np

In [3]: x = np.uint8([250])

In [4]: y = np.uint8([10])  

In [5]: print(cv2.add(x,y))    # 250+10 = 260 => 255
[[255]]

In [6]: print(x+y)    # 250+10 = 260 % 256 = 4
[4]

画像の組み合わせ

これも画像の足し算だが,組み合わせの比重を与えることができる.次式のように画像が加えられる.

{g(x) = (1-\alpha) f_0(x) + \alpha f_1(x)}

{\alpha}の値を{0→1}と変化させることで,ある画像がら次の画像への移り変わりを表現出来る.

次に示すものが2つの画像を組み合わせだ.1つ目の画像の比率を0.3,2つ目を0.7として,次式にcv2.addWeighted()を当てはめる.

{dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma}

ここでは{\gamma}の値は0をとっている.

In [1]: import cv2
In [2]: img1 = cv2.imread('ml.jpg')
In [3]: img2 = cv2.imread('lego.jpg')
In [4]: dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
In [5]: cv2.imwrite('blending.jpg',dst) 

結果はこんな感じに.

f:id:asdm:20151124184350j:plainf:id:asdm:20151124184353j:plainf:id:asdm:20151124184404j:plain

ビット演算

これらはビットごとの AND,OR,NOT,XOR 演算を含んでいる.これらは画像の一部を切り出す時や長方形でない領域を定義したり扱ったりする時にとても便利だ.これから画像の特定の領域をどうやって変えるのかの例を見ていこう.

In [1]: import cv2

In [2]: img1 = cv2.imread('messi.jpg')

In [3]: img2 = cv2.imread('logo.png')

In [4]: rows, cols, channels = img2.shape

In [5]: roi = img1[0:rows, 0:cols]

In [6]: img2gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)                                 

In [7]: ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)

In [8]: mask_inv = cv2.bitwise_not(mask)

In [9]: img1_bg = cv2.bitwise_and(roi, roi, mask = mask_inv)

In [10]: img2_fg = cv2.bitwise_and(img2, img2, mask = mask)                               

In [11]: dst = cv2.add(img1_bg, img2_fg)                                                  

In [12]: img1[0:rows, 0:cols] = dst

In [13]: cv2.imwrite('rslt.png', img1)                                                    
Out[13]: True

In [14]: # ついでにどうなってるかテスト

In [15]: cv2.imwrite('c', mask_inv)
Out[15]: True

In [16]: cv2.imwrite('img1_bg.png', img1_bg)
Out[16]: True

In [17]: cv2.imwrite('img2_fg.png', img2_fg)
Out[17]: True

結果は次のように.

rslt.png

f:id:asdm:20151207095002p:plain

mask_inv.png img1_bg.png img2_fg.png
f:id:asdm:20151207095047p:plain f:id:asdm:20151207095039p:plain f:id:asdm:20151207095017p:plain

追加資料

練習

cv2.addWeightedを使って,フォルダにある画像が滑らかに移り変わるスライドショーを作ってみよう.