#論考5 [和訳と要約](※Only興味のあるところ)Learning_OpenCV3_Computer_Vision_with_Python_2nd_Edition (1)

※Learning_OpenCV3_Computer_Vision_with_Python_2nd_Edition 
のフルヤ和訳→フルヤ要約(1)(※興味のあるところだけ抜粋)

 

"""
[動画画像_I/O基礎]
フォーマットに関わらず、Pixelは均等な単位を保持している
違いはどのように表現されるか。

"""
#例:2D(NumpyArray) GrayScale
import numpy as np
import cv2

img = np.zeros*1,
int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))

# videoWriter constructer
# 引数: filename,videoCodec,frameRate,frameSize(Class:VideoCapture→get()//)
videoWriter = cv2.VideoWriter('drop.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)

success,frame = videoCapture.read()
while success: # Loop until there are no more frames.
videoWriter.write(frame)
success, frame = videoCapture.read()

////
CF◆ chroma subsampled
クロマサブサンプリング
RGBだけで本来映像を表現する事ができる。
しかし明るさに対してInterestしたほうが人間工学的なので
輝度信号(Y)と2つの色信号(Cb,Cr)を別々に扱う。

CF◆constructer:videoCapture → .get(cv2.CAP_PROP_FPS)
バックエンドでサポートしていない対象の場合、0をしばしば返す
python標準時間測定の time.time()と併用して対策する
[sampleCode]
#時間の経過がなければ…初回にPython時間を杭打つ
#最終書き込みの def exitFrame(self)
if self._framesElapsed == 0:
self._startTime = time.time()
#二回目以降
#初回登録のPython時間から現状のPython時間を引き(1.timeElapsed)
#初回以降incrementされている_framesElapsed で割る(2._fpsEstimate)
#→ _fpsEstimate 割る timeElapsed
else:
timeElapsed = time.time() - self._startTime
self._fpsEstimate = self._framesElapsed / timeElapsed
self._framesElapsed += 1
 

"""

 

【顔追尾とImage操作】tracking / image manupulation on camera

並列する2つのInputを分析することで、リアルタイムでの顔認識を可能にします。
加えて録画済みのデータについてもこれらは適応可能です。
これらStream同士で検出された顔情報をSuperImpose、ブレンドされ
FilterやDistortionがリアルタイムで適応されます。
(The application will superimpose faces from one stream onto faces in the other...)


【オブジェクト志向型の動画分析】
これらリアルタイムでの顔編集はライブパフォーマンス的で、例えばDisneylandのアトラクションなどで多用されます。
OpenCVで動画がImageに還元される際には、SourceやDestinationと関係なく、
ほぼ全て同質にそれらを扱うことが可能です。
どのようにgetされsetされるにせよ、同様のロジックを適用することが可能です。

この際I/OのコードとApplicationのコードは分離して構築されます。
まず、CaptureManagerとWindowManagerというクラス。
ApplicationコードはCaptureManagerクラスを用いて新しいFrameを読み取り、
各フレームを任意の数のoutputへDispatchします。
この際送信されるデータは、
原型のimageFile、再構築されたVideoFile、或いはWindowManagerクラスを経由したWindow用表示データなどです

CaptureManager,WindowManagerは同時に拡張性と柔軟性を持っています。
カスタマイズすることで、OpenCVに頼らず、その他のフレームワークにも適応が可能です。

【VideoStreamの抽象化_CaptureManager】
OpenCVはVideo、Cameraより”Stream of Image”をキャプチャしたり、閲覧したり、操作したりする。
CaptureManagerクラスはそれらデータを抽象化し、Outputまでの流れを担うものだ。
CaptureManagerクラスは、VideoCaptureクラスと共に初期化。
クラス内包として、enterFrame()とexitFrame()Methodを持つ。
これらはApplication内のMainLoop中に呼び出されて機能する。
Applicationは呼び出しの最中に、”channel”Propertyをset,"frame"Propertyをgetするかもしれない

”channel”Propertyは初期値0。マルチヘッドカメラ使用時にことなる値を設定する可能性がある。
”frame”Propertyは、enterFrame()が呼ばれた再の最新のChannel情報に一致するImageデータを格納する。

同時にCaptureManagerは
WriteImage(),startWritingVideo(),stopWritingVideo() Method を持ち、柔軟にあらゆる側面で利用が可能です。
上記のメソッドは内部的処理を進めますが、実際のファイルライトは、exitFrame()の実行まで保留されています。
exitFrame()が実行された際は、"frame"Propertyがwindow表示される場合もあります。

その際、ApplicationCodeはWindowManagerクラスを、CaptureManagerのConstructerの引数として渡す。
或いは"previewWindowManager"Propertyを設定するかどちらかを行います。

もしAppllicationCodeがframeを操作する場合、
その操作は録画されたファイルかWindowの表示データに反映されます。
CaptureManagerクラスは引数とプロパティとしてshouldMirrorPreviewを持っています。
もしデータを鏡像として表示させたい際は、Trueに設定することでhorizontallyFlippedされたデータを表示することが可能です。

VideoWriterクラスがFPSのデータを必要とすることを思い出してください。
しかしながらOpenCVは正確なフレームレートを検出しません。
変わりにCaptureManagerクラスは、frameカウンターとPythonの認識している標準時間を用いることで
これを解決します。Pythonの標準時間は time.time() 関数で、必要となればこれを用いて時間計測を行います。
しかしながらこのやり方は安易なものではありません。
time.time()はシステム依存性があり、FPSは可変的なものだからです。
算出結果は不正確なものである場合も少なくありません。

まずはmanagers.py を制作します。
これはCaptureManager実行用です。

PythonはPrivateを定義しないので
)ここではUnderscoreで定義された変数を用意します。 例: self._enteredFrame.
これらは、現在読み取り中のframeやその他のファイルライト操作に関連したものでデコレータで定義されます
※@property は自分で定義したObjectから値を取得したり更新したり削除したりする。

enterFrame()は、Frameをglab(=Synchronizes)するのみ。
実際のchannelからの保存は後続するframe変数を待つことになる
exitFrame()はCurrentChannelからimageを取得。フレームレートを算出し
WindowManagerを経由してWindowに表示させる。

その一方でチャンネルからの確実な保存は、frame変数の読み込みを待っている。
そして、exitframe()は、現在のチャンネルのイメージを引き受け、FPSを見積もり、もしあればイメジをWindowManagerに表示。
そして保留されている全てのファイルへのイメージ書き込みを引き受ける。
以下に記述するその他のメソッドに関してはやはり(exitframe()同様、ファイル書き込みに関連している。

【cameoClass】
Application部分の実装に関しては、Cameoクラスが実装します。
Cameoクラスは2つのメソッドを持っています。run() onKeypress()です。
初期化の際、CameoクラスはWindowManagerクラスをonoKeypress()をコールバックとして構成します。
run()が呼ばれたとき、ApplicationはMainLoopを実行します。
この時MainLoopではframeとeventが実行されています。

eventの実行結果として、onKeypress()を呼ぶことも出来ます。
SpacebarでScreenshotを。TabでScreenCast(Recording)のStart/Stopを。
EscでApplicationを終了したりといった命令を与えることが出来ます。

Application起動時、Liveカメラのフィードはミラーされます。
しかし一方でscreenshotsやscreencastsはされません。
CaptureManager初期化の際、shouldMirrorPreview をTrueにしているからです。

【Prrocessing Images with OpenCV 3】
遅かれ早かれ、Imageそのものの編集は必要になります。

◆色空間と編集特性
Gray 色情報が極限まで少ないので、FaceDetectionに適している。
BGR 全てのpixelは8bit3列のArrayに格納される。Web系は親和性が高いがRGBの順番で扱う
HSV H:hueはtone。saturationは密集度、valueは明暗

◆BGR
[0 255 255](no blue, full green, and full red) →Yellow
となる。これは光の属性であって、もし絵の具でやったらマッディbrownになろう。
これはBGR(RGB)がadditiveだからだ。絵の具はsubtractiveである。
絵具にメディウムがあるように、monitorのメディウムは光であり、
moniterは光をemitすることでそれを表現するからだ。

【Fourier Transform】
フーリエ変換は様々な画像変換において中核になる。
フーリエは18世紀フランスの数学者で、様々な数学的発見をしている。
中でも熱原則に関する研究に集中しており、彼は全てをwaveformとした。
彼は全てのwaveformは、単純に異なる周波数の組み合わさった類同としたのだ。
言い換えるならば、私体の周囲で確認できるwaveformは、異なるwaveformsの総和なのだということ。
この概念は画像解析においては非常に有用である。
これによってpixelのシグナルが大きく変わる部分を領域境界線として認識することが可能だからだ。
この機能を用いて、ノイズかInterestか、backgroundかforegrandか等が判別しうる。
Numpyライブラリは離散フーリエ変換のMethod(fft2())を実装しており、
この複雑な計算を容易に処理することが出来る。

 

magniturde spectrum はフーリエ変換の原理から構成されている。
これはimageを変化の点から捉えるもので、
最も明るいpixelをcenterに集め、外縁に向けてグラデーションし端部で最も暗くなると考える
とわかりやすい。
この構図に変換することで、
1つのイメージの中にどれだけのlight/Dark_pixelが含まれているか、そのdistributionはどの程度か
を用意に知ることが出来る。

フーリエ変換はImage変換におけるあらゆるアルゴリズムの根本に位置している
例えばエッヂ検出やシェイプ検出。
具体例の簡易的なものとして、まずはHPFとLPFについて見てみたい。

◆HighPassFilter(HPF )

HPFはある領域に対して周囲のピクセルとの差異に応じて
intensityを付与するエフェクトです。
この仕組に酔って、対象範囲内はその他の領域と比べboostされます
言い換えればその部分は顕著に目立つようになるということです。
これは特別優秀なedge detectionです。


◆LowPassFilter
HPFがpixelIntensityを操作するのなら、
LPFは周囲との差異に応じて低い方を通す
これはでノイズやブラーに向いている
最もポピュラーなのはGaussianBlur 
※高周波数のデータを弱める(attenuate)することは、柔らかくする。

CF◆Convolution 畳み込み演算
足し算の結果を足し集める演算。マトリックスで指定される
→Operator,Filter,Mask,Kernel

【EdgeDetection】
Edgeは人間にとってもマシンにとっても重要だろう
Edgeによって私達は、逆光でも物体の種類やポーズを判別、ラフスケッチをする。
OpenCVはたくさんのエッヂ検出フィルターを持っている。
Laplacian(),Sobel(),Scharr()
これらは非エッジ領域を黒、エッジを白か強調色で彩色します。
しかし、これはしばしばノイズをエッヂと認識してしまうので不正確です。
この欠点に関しては、イメージをblurしてから処理を行うことで軽減できます。
blur(),medianBlur(),GaussianBlur()
同時にグレイスケールに変換することも効果的です。
通例

 

*1:3,3),dtype=np.uint8)

print(img)
 
【結果】
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]], dtype=uint8)
 
全てのpixelはsingle 8-bit integer 表現される。

つまり、全て0-255の尺度を持つ 

【numpy.arrayの活用】
much faster than a plain Python array.
item() method,
x(left)
y(top)
index:BGRのインデックス番号、色別の抽出が可能
itemset()はarg2に新しいValueを挿入可能

import numpy as np
import cv2

img.itemset( (150, 120, 0), 255)
img[:,:,1]=0 #Gを出力0
 
Python スライス操作について
基礎系
sequence[start:stop]
省略形
[:] → Whole (MutableListを取るときに有能なやり方)
s = "python"
>>> s[-2:]
'on'

【Regions Of Interests】
NumpyArrayIndexingの有効な活用として
regions of interests(ROI)おn策定が有ります。
この領域を変数格納した状態でOperationを加えていく
例えば他の領域への情報コピー等。
→フルヤメモ 自分の衣服に世界をあわせる(リアルタイムで)出来るのではないか?
※my_roi

import cv
import numpy as np
img = cv.imread('MyPic.png')


【Canny/EdgeDetection】
Algorhythm:quite complex but also interesting

1. denoises the image with Gaussian Filter
2. calculates gradient
3. applies non maximum suppression on edged
4. a double threshold on all the detected edges to eliminate false positives,lastly
5. analyzes all the edges and their connection to each other to keep the real edges and discard the weak ones

【Contour detection】
輪郭線検出は単純にそれ自体だけでなく、派生する操作にも必要になる。

roi = img[0:100,0:100]

フルヤ…たとえばこんな風に
roi = img[(pad):(img.shape[0]-pad),(pad):(img.shape[1]-pad)]


【VideoCapture基礎】VideoCapture/VideoWriterの2つのクラスが中核にある
read() によって呼び出されるVideoCaptureは1コマづつ動画の終わりまでframeを吐き出し続けます
→”デフォルトでBGRのimageファイル”(ココ重要)
逆に、書き出し方向はVideoWriterが担当しています。ファイルに情報を付与するのです。

////

import cv2

videoCapture = cv2.VideoCapture('drop.avi')
fps = videoCapture.get(cv2.CAP_PROP_FPS)
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
 
# videoWriter constructer
# 引数: filename,videoCodec,frameRate,frameSize(Class:VideoCapture→get()//)
videoWriter = cv2.VideoWriter('drop.avi',cv2.VideoWriter_fourcc('I','4','2','0'),fps,size)

success,frame = videoCapture.read()
while success: # Loop until there are no more frames.
videoWriter.write(frame)
success, frame = videoCapture.read()

////
CF◆ chroma subsampled
クロマサブサンプリング
RGBだけで本来映像を表現する事ができる。
しかし明るさに対してInterestしたほうが人間工学的なので
輝度信号(Y)と2つの色信号(Cb,Cr)を別々に扱う。

CF◆constructer:videoCapture → .get(cv2.CAP_PROP_FPS)
バックエンドでサポートしていない対象の場合、0をしばしば返す
python標準時間測定の time.time()と併用して対策する
[sampleCode]
#時間の経過がなければ…初回にPython時間を杭打つ
#最終書き込みの def exitFrame(self)
if self._framesElapsed == 0:
self._startTime = time.time()
#二回目以降
#初回登録のPython時間から現状のPython時間を引き(1.timeElapsed)
#初回以降incrementされている_framesElapsed で割る(2._fpsEstimate)
#→ _fpsEstimate 割る timeElapsed
else:
timeElapsed = time.time() - self._startTime
self._fpsEstimate = self._framesElapsed / timeElapsed
self._framesElapsed += 1

【minimum enclosing rectangle】
◆threshold の概念
単純に、閾値より高ければ白(輪郭判定用)、低ければ黒(後景)
 
subjectを包括する最小のrectangle and its circle.
cv2.findContours と他の機能との連携でこれは簡単に達成できます。
◆検出のプロセス
1.Loadしたイメージへのbinary threshold をgrayScaleVersionに適応
→Contour検出は全てgrayScaleのCopyに対して適応される

ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(),cv2.COLOR_BGR2GRAY)
,127,255,cv2.THRESH_BINARY)
[引数]
arg1 Source(grayScale only)
arg2 threshold
arg3 maximum value
arg4 option
[戻り値]
retVal 2値化用閾値
thresh 2値画像そのもの
 
 
●cv2.cvtColor 画像の色空間、imgそのものでなくimg.copy()を変換する
●cv2.threshold のargはGrayscaleのみ
ここのthreshold 値をいじればランタンみたいな動画が出来る。

2.まず全体を覆うシンプルなboundingBox の設定
A. 以下で検出
x.y.w.h = cv2.boundingRect(c)
B. 以下で線画
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

3.次に最小エリアの設定
OpenCVは、最小のrectangleのvertexes(頂点)を検出することは出来ません
なので、最小のrectangle areaを検出し、そこから算出します
この頂点はfloat型で認識されます。
しかしpipxelsはinteger型で認識されます