2015-0414 コンピュータビジョン Solem, J. (2012) 白井英俊 Programming Computer Vision with Python (相川訳「実践 コンピュータビジョン」オライリー・ジャパン, 2013) 訳者のまえがき 読みこなす上でのポイント 1.数学、特にベクトルと行列の計算に慣れていること 2.Python ― 習得しやすく生産性が高い。数学ライブラリがあり高速で正確 著者まえがき ガイドライン ・解説を読んだらすぐにサンプルコードを試す ・細部にこだわらず広い視点をもつ。理論よりも創発性を重視 前提条件 ・基本的なプログラミング、エディタの使い方、データ型、実行方法など ・基本的な数学。行列やベクトル演算、標準的な数学関数、微分係数や勾配の知識 本書で学べること ・Python を用いた画像処理の実践 ・各種のコンピュータビジョン技術 ・基礎的なアルギリズムの実装法や応用 サンプルコード:物体認識や類似画像検索、文字認識、オプティカルフロー、追跡 (トラッキング) 、3 次元復元、ステレオ画像、拡張現実感(AR:Augmented Reality)、ジェ スチャー認識、パノラマ画像合成、画像のセグメンテーション(領域分割)、ノイズ除去、画像 分類など コンピュータビジョンとは 画像から情報を自動抽出すること 「情報」: 3D モデル、カメラ位置、物体検出や認識、画像内容のグループ化や検索など、さらに 画像のワーピングやノイズ除去、拡張現実感なども 使用する Python: バージョン 2.7 ベクトルや行列: NumPy というモジュールの配列 array 型で表す 処理結果の可視化:Matplotlib 数式: インライン表記 f (x) = w Tx + b もしくは 𝑓𝑓 (𝐱𝐱) = ∑𝑖𝑖 𝑤𝑤𝑖𝑖 𝑥𝑥𝑖𝑖 + 𝑏𝑏 スカラーを小文字(s、r、λ、θ、...)、行列を大文字(A、V、H、...)、ベクトルを太字の小 文字(t、c、...) 画像を配列で表す場合は I 、2D 上の点は x = [x,y]、3D 上の点は X = [X,Y,Z ] 2015-0414 コンピュータビジョン 白井英俊 1章 基本的な画像処理 画像の扱い方や処理方法, Python の主要なパッケージ、画像の読み込み・変換、拡大縮 小、微分、表示、結果の保存などのための基本的なツールの紹介 1.1 PIL(Python Imaging Library) 一般的な画像の取り扱いのほか、サイズ変更、切り抜き、回転、色変換といった画像の基 本操作 画像ファイル形式の読み込み、保存 (表示はできない?) Image モジュール:最も重要なモジュール ――― 読み込みの例 from PIL import Image pil_im = Image.open('empire.jpg') # 戻り値は PIL 画像オブジェクト 色変換: convert() メソッド pil_im = Image.open('empire.jpg').convert('L') #読み込んでグレースケール変換 サンプルコード; http://www.pythonware.com/library/pil/handbook/index.htm (図 1.1) 1.1.1 画像のファイル形式変換 画像の保存:save( ) メソッド サンプルコード: from PIL import Image # PIL モジュールから Image を使えるようにする import os # os モジュールを使えるようにする filelist = ['foo.jpg', 'bar.bmp', 'zot.png'] # 画像ファイルのリスト for infile in filelist: # それぞれのファイルに対して outfile = os.path.splitext(infile)[0] + ".jpg" # 出力ファイルを指定 if infile != outfile: # 入力ファイルが jpg ファイルでなければ try: # エラーが起きた時の対処 Image.open(infile).save(outfile) # 画像ファイルを開いて保存 except IOError: # ファイルが開けない場合のエラー print "cannot convert", infile この本のサンプルコード imtools.py に。ディレクトリの中のすべての JPEG 画像ファイ ル名のリストを取得する関数が定義されている: import os def get_imlist(path): """ path に指定されたディレクトリのすべての jpg ファイル名のリストを返す """ return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')] 使用例 filelist = imtools.get_imlist('.') # 現在のディレクトリ(‘.’)の JPEG ファイルリストを返す 2015-0414 コンピュータビジョン 1.1.2 サムネイルの作成 pil_im.thumbnail((128,128)) 白井英俊 #幅と高さの最大値が 128 ピクセルのサムネイル生成 1.1.3 領域のコピーと貼りつけ PIL は左上隅を(0,0)とする座標系を採用 box = (100,100,400,400) region = pil_im.crop(box) # crop で画像の切り抜き(引数は(左,上,右,下)の座標値) 貼り付け: region = region.transpose(Image.ROTATE_180) # 180 度回転 pil_im.paste(region,box) # box で指定された範囲に画像 region を貼り付け 1.1.4 拡大縮小と回転 画像のサイズ変更: out = pil_im.resize((128,128)) 画像の回転: out = pil_im.rotate(45) # resize 関数、引数は幅と高さのサイズ # rotage 関数。引数は時計回りの角度 例:図 1-1-左から、原画像、グレースケール化したもの、一部を切り抜き回転して貼 りつけたもの、サムネイル画像 1.2 Matplotlib グラフを描いたり画像に点や直線、曲線を描画するためのモジュール(PIL よりも強力) PyLab インタフェース: グラフ描画用の関数群 1.2.1 画像と点と線を描画する 画像と点と線を描画する例 from PIL import Image from pylab import * # 配列に画像を読み込む # import * には注意せよ(あまり使わない方が良い) # import pylab as pl などとするほうがよい im = array(Image.open('empire.jpg')) # pylab.array # 画像を表示するための準備(show で表示) imshow(im) # 点の座標 # pylab.imshow x = [100,100,400,400] y = [200,500,200,500] # 赤い星マークで点を描画する plot(x,y,'r*') # pylab.plot 2015-0414 コンピュータビジョン 白井英俊 # 最初の 2 点間に線を描画する plot(x[:2],y[:2]) # pylab.plot # タイトルを追加し、描画結果を表示する title('Plotting: "empire.jpg"') # pylab.title show() # pylab.show() #通常、1 つのスクリプトにつき 1 回だけ、スクリプトの末尾で呼び出す #スクリプトの実行をブロックし、ウィンドウが閉じられるまでスクリプトを停止 座標軸の非表示: axis('off') Plot 関数の引数の例: 線: ‘-‘ 実線、’- -‘ 破線、’:’ 点線 色: b 青,g 緑,r 赤,c シアン、m マゼンタ、y 黄、k 黒、w 白) マーカ: . 点、o 丸、s 四角、* 星、+ 十字、x ✕ plot(x,y) # デフォルトの青の実線 plot(x,y,'r*') # 赤の星マーク plot(x,y,'go-') # 緑の線で、丸マーク付き plot(x,y,'ks:') # 黒の点線で、四角マーク付き 1.2.2 画像の等高線とヒストグラム 等高線はすべての座標[x,y]について 1 つの値を要求→グレースケールの画像が必要 from PIL import Image from pylab import * # 画像を配列に読み込む im = array(Image.open('empire.jpg').convert('L')) # pylab.array # 新しい図を作成する figure() # pylab.figure # 色を使わない gray() # pylab.gray() # 左上隅を原点とする等高線を表示する contour(im, origin='image') axis('equal') # pylab.axis axis('off') # pylab.axis # pylab.contour ヒストグラム: ピクセル値の分布を描画(ビンの数を指定) figure() # pylab.figure hist(im.flatten(),128) # 引数は「1 次元の array」と「ビンの数」 pylab.hist show() # pylab.show flatten()メソッドは、多次元配列の行方向に値をとって 1 次元の配列に変換 2015-0414 コンピュータビジョン 白井英俊 1.2.3 インタラクティブな注釈 ginput()関数: 簡単に点を入力できる 例:画像を表示した後、ユーザーがウィンドウ上で画像領域を 3 回クリックするのを待ち、 クリックされた座標[x,y]をリスト x に保存 from PIL import Image from pylab import * im = array(Image.open('empire.jpg')) # pylab.array imshow(im) # pylab.imshow print '3 点クリックしてください' x = ginput(3) #pylab.ginput print 'クリックした座標:',x show() # pylab.show 1.3 NumPy :科学技術計算パッケージ NumPy には、ベクトルや行列、画像などを表現するための配列オブジェクトや線形代数 の関数など、便利な機能がある 配列オブジェクトにより、行列の積や転置行列の作成、方程式の解法、ベクトルの積、正 規化などが可能---用途: 画像変換、ワーピング、変形モデリング、画像認識、グループ分け 1.3.1 配列による画像表現 NumPy の配列は多次元: ベクトルや行列、画像を表現可能 配列は「すべての要素が同じ型でなければならない」制約があるリストとみなせる 配列: shape の例 (800, 569, 3) --- 各次元の大きさ(行数,列数,色数) dtype の例 uint8 --- 配列の要素のデータ型を表す文字列 通常、画像は符号なし 8 ビット整数(uint8)、浮動小数点数(float32)にするには im = array(Image.open('empire.jpg').convert('L'),'f') のように’f ’指定が必要 配列の要素には添字を使ってアクセス: 例 value = im[i,j,k] 配列のスライスを使って複数の要素にアクセス 例: im[i,:] = im[j,:] # j 行の値を i 行に代入する im[:,i] = 100 # i 列のすべての値を 100 にする # 座標(i,j)、色 k の値 im[:100,:50].sum() # 最初の 100 行と 50 列の値の合計 im[50:100,50:100] # 50~100 行と、50~100 列(100 番目は含まれない) im[i].mean() # i 行の平均 im[:,-1] # 最後の列 im[-2,:] (or im[-2]) # 最後から 2 番目の行 2015-0414 コンピュータビジョン 白井英俊 添字が 1 つだけのときは、行の添字として解釈 マイナスの添字は末尾から逆方向に数える 1.3.2 グレーレベルの変換 画像を Numpy 配列に読み込むことで、数値演算が可能 例としてグレーレベルの変換 from PIL import Image from numpy import * # import numpy as np とすることが多い im = array(Image.open('empire.jpg').convert('L')) # np.array im2 = 255 - im # 画像を反転する im3 = (100.0/255) * im + 100 # 100~200 の値に縮める im4 = 255.0 * (im/255.0)**2 # 2 乗する:暗いピクセルをより暗く print int(im.min()), int(im.max()) # np.int ピクセル値の最大値と最小値 配列から画像に戻す pil_im = Image.fromarray(im) # 配列から PIL 画像に戻す pil_im = Image.fromarray(uint8(im)) #、「uint8」以外のデータ型に変換した場合 参考: 画像表示には pylab の imshow と show を用いる 1.3.3 画像のサイズ変更 、配列のサイズを変えるのは容易ではないため、サイズ変更関数を用意: def imresize(im,sz): # imtools.py で定義 """ PIL を使って画像配列のサイズを変更する """ pil_im = Image.fromarray(uint8(im)) 1.3.4 ヒストグラム平坦化 グレースケールの画像とヒストグラムのビン数を渡すと、ヒストグラム平坦化された画像 と、ピクセル値の変換に用いられた累積分布関数を返す関数 def histeq(im,nbr_bins=256): # imtools.py で定義 """ グレースケール画像のヒストグラム平坦化 """ # 画像のヒストグラムを得る imhist,bins = histogram(im.flatten(),nbr_bins,normed=True) cdf = imhist.cumsum() # 累積分布関数 cdf = 255 * cdf / cdf[-1] # 正規化 # cdf を線形補間し、新しいピクセル値とする im2 = interp(im.flatten(),bins[:-1],cdf) return im2.reshape(im.shape), cdf 2015-0414 コンピュータビジョン 白井英俊 使用例: コントラストが増して暗い領域が明確になる from PIL import Image from numpy import * im = array(Image.open('AquaTermi_lowcontrast.jpg').convert('L')) im2,cdf = imtools.histeq(im) 1.3.5 平均画像 画像列から平均画像を求める:画像はすべて同じサイズと仮定し、総和を画像数で割る def compute_average(imlist): # imtools.py で定義 """ 画像列の平均を求める """ # 最初の画像を開き、浮動小数点数の配列に変換する averageim = array(Image.open(imlist[0]), 'f') for imname in imlist[1:]: try: averageim += array(Image.open(imname)) except: print imname + '...skipped' averageim /= len(imlist) # 平均を uint8 に変換する return array(averageim, 'uint8') 1.3.6 画像の主成分分析 主成分分析(Principal Component Analysis:PCA) :次元を削減するのに便利な方法 訓練データの多様性をできるだけ少ない次元数で表現するのに最適 PCA で出力された写像行列は、重要度順に並んだ座標軸に座標値を変換するものとみな せる 画像に PCA を適用するには、NumPy の flatten()メソッドなどにより、画像を 1 次元の ベクトルに変換する 主成分を計算する前に、各行を平均画像からの相対値に変換(平均画像が原点になるよう にセンタリング), その後で特異値分解(Singular Value Decomposition:SVD) pca.py の内容 -------------------------------------------------------------------------------------------------------------------from PIL import Image from numpy import * def pca(X): """ 主成分分析 入力:X, 訓練データを平板化した配列を行として格納した行列 出力:写像行列(次元の重要度順), 分散, 平均 """ # 次元数を取得 2015-0414 コンピュータビジョン 白井英俊 num_data,dim = X.shape # データをセンタリング mean_X = X.mean(axis=0) X = X - mean_X # 平均を引くことでセンタリング if dim>num_data: # PCA - 高次元のときはコンパクトな裏技を用いる M = dot(X,X.T) # 共分散行列 e,EV = linalg.eigh(M) # 固有値と固有ベクトル tmp = dot(X.T,EV).T # ここがコンパクトな裏技 V = tmp[::-1] # 末尾の固有ベクトルほど重要なので、反転する S = sqrt(e)[::-1] # 固有値の並びも反転する for i in range(V.shape[1]): V[:,i] /= S else: # PCA - 低次元なら特異値分解を用いる U,S,V = linalg.svd(X) V = V[:num_data] # 最初の num_data の分だけが有用 # 写像行列と、分散、平均を返す return V,S,mean_X -------------------------------------------------------------------------------------------------------------------pca.py を用いて主成分分析を行う: fontimages.zip を使用 from PIL import Image from numpy import * from pylab import * import pca im = array(Image.open(imlist[0])) # サイズを得るため画像を 1 つ開く m,n = im.shape[0:2] # 画像のサイズを得る imnbr = len(imlist) # 画像数を得る # すべての平板化画像を格納する行列を作る immatrix = array([array(Image.open(im)).flatten() for im in imlist],'f') # 主成分分析を実行する V,S,immean = pca.pca(immatrix) # 画像を表示する(平均と、最初の 7 つの主成分) figure() gray() subplot(2,4,1) imshow(immean.reshape(m,n)) 2015-0414 コンピュータビジョン for i in range(7): subplot(2,4,i+2) imshow(V[i].reshape(m,n)) # 1 次元表現から画像に戻す show() 1.3.7 pickle モジュール pickle: Python オブジェクトを文字列表現に変換、その逆は unpickle import pickle # 平均と主成分を保存する f = open('font_pca_modes.pkl', 'wb') pickle.dump(immean,f) pickle.dump(V,f) # immean, V の順で記憶 f.close() 保存したデータからの読み出し: load()メソッド import pickle # 平均と主成分を読み出す f = open('font_pca_modes.pkl', 'rb') immean = pickle.load(f) V = pickle.load(f) # immean, V の順で取り出し f.close() オブジェクトの順番を同じにすることに注意 ファイルの読み書きのための with ステートメント # ファイルを開いて保存する import pickle with open('font_pca_modes.pkl', 'wb') as f: pickle.dump(immean,f) pickle.dump(V,f) # ファイルを開いて読み込む import pickle with open('font_pca_modes.pkl', 'rb') as f: immean = pickle.load(f) V = pickle.load(f) 白井英俊 2015-0414 コンピュータビジョン 白井英俊 1.4 SciPy : NumPy 上に構築されたオープンソースの数学パッケージ 1.4.1 画像をぼかす: 画像の畳み込みの例 Iσ= I *Gσ ガウシアンカーネルを用いた(グレースケール)画像 I の畳み込み Gσ = 1 2𝜋𝜋𝜋𝜋 𝑒𝑒 −(𝑥𝑥 2 +𝑦𝑦2 ) /2𝜎𝜎 2 scipy.ndimage.filters: 高速な 1 次元分割により、畳み込みを計算 from PIL import Image from numpy import * from scipy.ndimage import filters im = array(Image.open('empire.jpg').convert('L')) im2 = filters.gaussian_filter(im,5) # 標準偏差を 5 とした カラー画像をぼかすには、色チャンネルごとにガウシアンぼかし im = array(Image.open('empire.jpg')) im2 = zeros(im.shape) for i in range(3): im2[:,:,i] = filters.gaussian_filter(im[:,:,i],5) im2 = uint8(im2) 1.4.2 画像の微分 画像の勾配は、∇I = [I x, I y]T 画像の微分を計算するには離散近似 Ix= I *Dx Iy= I *Dy 代表的なものは Prewitt フィルタと Sobel フィルタ scipy.ndimage.filters モジュール標準の畳み込みを使って、簡単に実装できる from PIL import Image from numpy import * from scipy.ndimage import filters im = array(Image.open('empire.jpg').convert('L')) # Sobel 微分係数フィルタ imx = zeros(im.shape) filters.sobel(im,1,imx) imy = zeros(im.shape) filters.sobel(im,0,imy) magnitude = sqrt(imx**2+imy**2) sobel()関数の第 2 引数により x か y の微分の方向を選択し、第 3 引数に出力を格納 正の微分係数は明るいピクセル、負の微分係数は暗いピクセル、灰色の領域は 0 に近い値 2015-0414 コンピュータビジョン 白井英俊 この方式の欠点:微分係数が画像の解像度に依存 ガウシアン微分フィルタ:ノイズにロバスト、いろいろな解像度に対応 Ix= I *Gσx Iy= I * Gσy Gx と Gy : x 方向と y 方向の標準 のガウス関数 G filters.gaussian_filter()関数は、次のように引数を追加によりガウシアン微分を計算可能 sigma = 5 # 標準偏差 imx = zeros(im.shape) #第 3 引数に微分の方向を指定し、第 2 引数に標準偏差 filters.gaussian_filter(im, (sigma,sigma), (0,1), imx) imy = zeros(im.shape) filters.gaussian_filter(im, (sigma,sigma), (1,0), imy) 1.4.3 モルフォロジー: 物体を数える 通常、モルフォロジーは 2 値画像だが、グレースケールにも適用可能 scipy.ndimage モジュールの morphology scipy.ndimage モジュールの measurements :2 値画像の計数や測定関数 物体を数える: from scipy.ndimage import measurements,morphology # 画像を読み込み、閾値処理で 2 値化する im = array(Image.open('houses.png').convert('L')) im = 1*(im<128) # 2 値の配列に変換 labels, nbr_objects = measurements.label(im) print "Number of objects:", nbr_objects # モルフォロジー 物体を分離する # binary_opening()の第 2 引数は構造要素:y 方向に 9 ピクセル(上下に 4 ずつ)、x 方向に 5 ピクセル im_open = morphology.binary_opening(im,ones((9,5)),iterations=2) labels_open, nbr_objects_open = measurements.label(im_open) print "Number of objects:", nbr_objects_open 非 0 の要素が隣接ピクセルとして扱われる 1.4.4 便利な SciPy モジュール io, misc : 入出力のためのモジュール scipy.io.loadmat : Matlab の .mat 形式のデータ読み込み scipy.io.savemat : Matlab の .mat 形式のデータ保存 2015-0414 コンピュータビジョン 白井英俊 scipy.misc モジュールの imsave()関数 : 配列を画像ファイルに保存 scipy.misc.lena() : Lena(512×512 のグレースケール画像) 1.5 高度な例:画像のノイズ除去 画像のノイズ除去: 詳細や構造を保存しながら画像のノイズを除去する処理 Rudin-Osher-Fatemi(ROF)のノイズ除去モデルを用いた例 グレースケール画像 I の Total Variation(TV)を勾配のノルムの和として定義 J(I) = ∫|∇𝐼𝐼 |𝑑𝑑𝒙𝒙 ノイズ除去した画像 U は以下の値を最小化 min‖𝐼𝐼 − 𝑈𝑈‖2 + 2λ𝐽𝐽(𝑈𝑈) 𝑈𝑈 rof.py の中の denoise() 関数 応用例: from PIL import Image from pylab import * import rof im = array(Image.open('empire.jpg').convert('L')) U,T = rof.denoise(im,im) figure() gray() imshow(U) axis('equal') axis('off') show() 演習問題をやってみよう
© Copyright 2024 Paperzz