opencv チュートリアルチャレンジ13 ハフ変換による直線検出
ハフ変換による直線検出 — OpenCV-Python Tutorials 1 documentation
直線を検出したい
cv2.HoughLines
標準ハフ変換
void HoughLines(Mat& image, vector<Vec2f>& lines, double rho, double theta, int threshold, double srn=0, double stn=0) 標準ハフ変換を用いて,2値画像から直線を検出します. パラメタ: image – 8ビット,シングルチャンネルの2値入力画像.この画像は関数により書き換えられる可能性があります. lines – 検出された直線が出力されるベクトル.各直線は,2要素のベクトル (\rho, \theta) で表現されます. \rho は原点(画像の左上コーナー)からの距離, \theta はラジアン単位で表される直線の回転角度(0 \sim 垂直線, \pi/2 \sim 水平線)です. rho – ピクセル単位で表される投票空間の距離分解能. theta – ラジアン単位で表される投票空間の角度分解能. threshold – 投票の閾値パラメータ.十分な票( >\texttt{threshold} )を得た直線のみが出力されます. srn – マルチスケールハフ変換において,距離分解能 rho の除数となる値.投票空間の粗い距離分解能は rho となり,細かい分解能は rho/srn となります.もし srn=0 かつ stn=0 の場合は,古典的ハフ変換が利用されます.そうでない場合は,両方のパラメータが正値である必要があります. stn – マルチスケールハフ変換において,角度分解能 theta の除数となる値.
やってみる
lines = cv2.HoughLines(edges, 1, np.pi/180, 200) for line in lines: rho, theta = line[0] a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv2.line(img,(x1,y1),(x2,y2),(0,0,255),1)
これが
こう
cv2.HoughLinesP
確率的ハフ変換
void HoughLinesP(Mat& image, vector<Vec4i>& lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0) 確率的ハフ変換を利用して,2値画像から線分を検出します. パラメタ: image – 8ビット,シングルチャンネルの2値入力画像.この画像は関数により書き換えられる可能性があります. lines – 検出された線分が出力されるベクトル.各線分は,4要素のベクトル (x_1, y_1, x_2, y_2) で表現されます.ここで (x_1,y_1) および (x_2, y_2) は,検出された各線分の端点です. rho – ピクセル単位で表される投票空間の距離分解能. theta – ラジアン単位で表される投票空間の角度分解能. threshold – 投票の閾値パラメータ.十分な票( >\texttt{threshold} )を得た直線のみが出力されます. minLineLength – 最小の線分長.これより短い線分は棄却されます. maxLineGap – 2点が同一線分上にあると見なす場合に許容される最大距離.
やってみる
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 200, 100, 10) for line in lines: x1,y1,x2,y2 = line[0] cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)
これが
こう
opencv チュートリアルチャレンジ3 画像のしきい値処理
画像のしきい値処理 — OpenCV-Python Tutorials 1 documentation
元画像
cv2.threshold
この関数は,シングルチャンネルの配列に対して,ある定数での閾値処理を行います.これは,グレースケールからの2値画像生成(関数 compare も,この目的に利用できます)やノイズ除去(つまり,小さすぎたり大きすぎたりする値をはじく処理)などに利用される場合が多いです.この関数がサポートする閾値処理にはいくつかの種類があり,それは引数 thresholdType によって決定されます:
double threshold(const Mat& src, Mat& dst, double thresh, double maxVal, int thresholdType) 配列の要素に対して,ある定数での閾値処理を行います. パラメタ: src – 入力配列(シングルチャンネル,8ビット,あるいは32ビット浮動小数点型). dst – src と同じサイズ,同じ型の出力配列. thresh – 閾値. maxVal – 閾値処理の種類が THRESH_BINARY や THRESH_BINARY_INV の場合に利用される,最大値の値. thresholdType – 閾値処理の種類(以下の説明を参照してください).
やってみる
ret, img = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY)
ret, img = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cv2.adaptiveThreshold
適応的閾値処理
void adaptiveThreshold(const Mat& src, Mat& dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C) 配列に対して,適応的な閾値処理を行います. パラメタ: src – 8ビット,シングルチャンネルの入力画像. dst – src と同じサイズ,同じ型の出力画像. maxValue – 条件を満足するピクセルに割り当てられる非0の値.説明を参照してください. adaptiveMethod – 利用される適応的閾値アルゴリズム: ADAPTIVE_THRESH_MEAN_C または ADAPTIVE_THRESH_GAUSSIAN_C (説明を参照してください). thresholdType – 閾値の種類. THRESH_BINARY または THRESH_BINARY_INV のどちらか. blockSize – ピクセルの閾値を求めるために利用される近傍領域のサイズ.3, 5, 7, など. C – 平均または加重平均から引かれる定数(説明を参照).通常,これは正値ですが,0や負値の可能性もあります.
やってみる
img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, 20)
opencv チュートリアルチャレンジ9 OpenCVにおける輪郭(領域)
輪郭に関するチュートリアル
OpenCVにおける輪郭(領域) — OpenCV-Python Tutorials 1 documentation
輪郭抽出
OpenCVの輪郭検出は,黒い背景から白い物体の輪郭を検出すると仮定しています.物体は白(明るい色),背景は黒(暗い色)と覚えておいてください.
とある。
cv2.findContours
輪郭抽出と、その輪郭に対する処理を人多りやてみる。
void findContours(const Mat& image, vector<vector<Point> >& contours, int mode, int method, Point offset=Point()) 2値画像中の輪郭を検出します. パラメタ: image – 入力画像,8ビット,シングルチャンネル.0以外のピクセルは 1として,0のピクセルは0のまま扱われます.つまり,入力画像は 2値画像 として扱われます.グレースケールやカラー画像から2値画像を得るには, compare() , inRange() , threshold() , adaptiveThreshold() , Canny() などの関数を利用します.また,この関数は,輪郭抽出処理中に入力画像 image の中身を書き換えます. contours – 検出された輪郭.各輪郭は,点のベクトルとして格納されます. hiararchy – オプション.画像のトポロジーに関する情報を含む出力ベクトル.これは,輪郭数と同じ数の要素を持ちます.各輪郭 contours[i] に対して,要素 hierarchy[i][0] , hiearchy[i][1] , hiearchy[i][2] , hiearchy[i][3] にはそれぞれ,同じ階層レベルに存在する前後の輪郭,最初の子輪郭,および親輪郭の contours インデックス(0 基準)がセットされます.また,輪郭 i において,前後,親,子の輪郭が存在しない場合,それに対応する hierarchy[i] の要素は,負の値になります. mode – 輪郭抽出モード CV_RETR_EXTERNAL 最も外側の輪郭のみを抽出します.すべての輪郭に対して hierarchy[i][2]=hierarchy[i][3]=-1 がセットされます. CV_RETR_LIST すべての輪郭を抽出しますが,一切の階層構造を保持しません. CV_RETR_CCOMP すべての輪郭を抽出し,それらを2階層構造として保存します:上のレベルには,連結成分の外側の境界線が,下のレベルには,連結成分の内側に存在する穴の境界線が属します.ある連結成分の穴の内側に別の輪郭が存在する場合,その穴は上のレベルに属します. CV_RETR_TREE すべての輪郭を抽出し,入れ子構造になった輪郭を完全に表現する階層構造を構成します.この完全な階層構造は,OpenCVの contours.c デモで見ることができます. method – 輪郭の近似手法: CV_CHAIN_APPROX_NONE すべての輪郭点を完全に格納します.つまり,この手法により格納された任意の隣り合う2点は,互いに8近傍に存在します. CV_CHAIN_APPROX_SIMPLE 水平・垂直・斜めの線分を圧縮し,それらの端点のみを残します.例えば,まっすぐな矩形の輪郭線は,4つの点にエンコードされます. CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS Teh-Chinチェーン近似アルゴリズムの1つを適用します. TehChin89 を参照してください. offset – オプションのオフセット.各輪郭点はこの値の分だけシフトします.これは,ROIの中で抽出された輪郭を,画像全体に対して位置づけて解析する場合に役立ちます.
通常、グレースケール化して、輪郭を抽出して、見やすいように色を載せると思う。
面倒なので、カラー化したものを変換して乗せてしまう。
元の画像は、これ。
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np import math img = cv2.imread('170519-174830.jpg') img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) img = cv2.GaussianBlur(img, (11, 11), 0) #cv2.imshow('GaussianBlur', img) #ret, img = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY) #cv2.imshow('threshold1', img) #cv2.imwrite('threshold1.png', img) #ret, img = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY) #cv2.imshow('threshold2', img) #cv2.imwrite('threshold2.png', img) img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 15, 5) cv2.imshow('adaptiveThreshold', img) #cv2.imwrite('adaptiveThreshold.png', img) #img, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print("{} contours.".format(len(contours))) # カラー化 img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) #img = cv2.drawContours(img, contours, -1, (0,0,255), 1) #cv2.imshow('drawContours', img) #cv2.imwrite('drawContours.png', img) if len(contours) > 0: for i, contour in enumerate(contours): #print("{}, {}".format(i, len(contour))) # area:面積 area = cv2.contourArea(contour) if area < 100: continue # moments:重心 M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) else: cx, cy = 0, 0 # 重心を中心にした円 cv2.circle(img, (cx, cy), int(math.sqrt(area)), (0,255,0), 1, 8) # 最小外接円 (x, y), radius = cv2.minEnclosingCircle(contour) center = (int(x),int(y)) radius = int(radius) img = cv2.circle(img,center,radius,(255,255,0),2) # 輪郭の近似 epsilon = 0.05*cv2.arcLength(contour,True) approx = cv2.approxPolyDP(contour,epsilon,True) print(len(approx)) if len(approx) == 4: cv2.drawContours(img, [approx], -1, (255, 0, 0), 3) # 凸包 hull = cv2.convexHull(contour) img = cv2.drawContours(img,[hull],0,(255,255,0),2) # 外接矩形 rect = cv2.boundingRect(contour) cv2.rectangle(img, (rect[0], rect[1]), (rect[0]+rect[2], rect[1]+rect[3]), (0,0,255), 1) # 回転を考慮した外接矩形 rect = cv2.minAreaRect(contour) box = cv2.boxPoints(rect) box = np.int0(box) img = cv2.drawContours(img,[box],0,(0,255,255),1) # 楕円のフィッティング ellipse = cv2.fitEllipse(contour) img = cv2.ellipse(img,ellipse,(0,255,0),2) #直線のフィッティング rows,cols = img.shape[:2] [vx,vy,x,y] = cv2.fitLine(contour, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2) cv2.imshow('findContours', img) cv2.imwrite('findContours.png', img) cv2.waitKey(0) cv2.destroyAllWindows()
こうなった
キーワード
Extent
Solidity
等価直径
傾き
マスクと画素点
最大値,最小値とその位置
平均色と平均値
端点
凸性の欠陥
こんどやる
輪郭の階層情報
いつかやる
opencv チュートリアルチャレンジ7 Canny法によるエッジ検
Canny法によるエッジ検出 — OpenCV-Python Tutorials 1 documentation
cv2.Canny
void cv::Canny ( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false ) image 8-bit input image. edges output edge map; single channels 8-bit image, which has the same size as image . threshold1 first threshold for the hysteresis procedure. threshold2 second threshold for the hysteresis procedure. apertureSize aperture size for the Sobel operator. L2gradient a flag, indicating whether a more accurate ....
やってみる
元画像
edges = cv2.Canny(img_gray, 50, 150, apertureSize=3)
edges = cv2.Canny(img_gray, 20, 100, apertureSize=3)
edges = cv2.Canny(img_gray, 50, 150, apertureSize=7)
keras-rl を試す
keras-rl を試します。
Git
インストール
$ sudo pip install keras $ sudo pip install keras-rl
テスト
まず、サンプルプログラムを入手します。
$ git clone https://github.com/matthiasplappert/keras-rl.git
私は tensorflow を virtualenv で入れましたので、、、
$ cd tensorflow/ $ source ~/tensorflow/bin/activate (tensorflow)$ python ~/github/keras-rl/examples/dqn_cartpole.py
を、コンソールから行います。
Chainer Linear を確認する
Chainer Linear を確認する
理解が進む度に、書き加えていく。
サンプルスクリプト
y = 2x + 2
を想定したもの。
#!/usr/bin/env python # coding:utf-8 import numpy as np import chainer.functions as F import chainer.links as L from chainer import Variable, optimizers # モデル定義 model = L.Linear(1, 1) optimizer = optimizers.SGD() optimizer.setup(model) # 学習させる回数 times = 1000 # 入力ベクトル x = Variable(np.array( [[1],[3],[5],[7]], dtype=np.float32)) #x = Variable(np.array( [[1]], dtype=np.float32)) # 正解ベクトル t = Variable(np.array( [[2],[6],[10],[14]], dtype=np.float32)) #t = Variable(np.array( [[2]], dtype=np.float32)) # 学習ループ for i in range(0, times): optimizer.zero_grads() # 勾配を初期化 y = model(x) # モデルに予測させる loss = F.mean_squared_error(y, t) # 損失を計算 print("Data: {}, Loss: {}".format(y.data, loss.data) ) loss.backward() # 逆伝播する optimizer.update() # optimizer を更新する print("Weight : {}".format(model.W.data) ) print("Bias : {}".format(model.b.data) ) print("---TEST---") x = Variable(np.array( [[3],[4],[5]], dtype=np.float32) ) y = model(x) print("Test data : {}".format(x.data) ) print("Test result : {}".format(y.data) )
実行
$ python 001.py ・・・ Data: [[ 3.98719549] [ 7.99330425] [ 11.99941349] [ 16.00552177]], Loss: 5.99056111241e-05 Weight : [[ 2.00304031]] Bias : [ 1.98421395] ---TEST--- Test data : [[ 3.] [ 4.] [ 5.]] Test result : [[ 7.99333477] [ 9.99637508] [ 11.9994154 ]]
ということで、
y = 2.00304031 x + 1.98421395
と、作成されました!
ソースコード
/usr/local/lib/python2.7/dist-packages/chainer/links/connection/linear.py
/usr/local/lib/python2.7/dist-packages/chainer/functions/connection/linear.py
APIから
Linearモデル
コンスタント
class chainer.links.Linear(in_size, out_size, wscale=1, bias=0, nobias=False, initialW=None, initial_bias=None)
とのこと。
今回は model = L.Linear(1, 1)
として、 in_size=1, out_size=1 で作っている。
class Linear(link.Link):
とのことで、link.Link
を継承しているらしい。
call
model(x)
すると、 __call__
が呼ばれるわけだけど、そのなかで
from chainer.functions.connection import linear ・・・ return linear.linear(x, self.W, self.b)
なので、結局のところ何をするのかは、 functions/connection/linear.py
を見る。
def linear(x, W, b=None): if b is None: return LinearFunction()(x, W) else: return LinearFunction()(x, W, b)
コメントをカットしたらこうなった。
LinearFunction()(x, W)
をみて、ぎょっとしたが、これは、LinearFunction インスタンスを作成して、その ___call___
を読んでいる。
class LinearFunction(function.Function):
とのことなので、 Function をみよう。
/usr/local/lib/python2.7/dist-packages/chainer/function.py
をみる。
class Function(object): ・・・ def __call__(self, *inputs): """Applies forward propagation with chaining backward references.
とのこと。
cuda が使えるかどうかなどで条件分岐されているが、forward するんだろと思う。
forward
def forward(self, inputs): x = _as_mat(inputs[0]) W = inputs[1] y = x.dot(W.T).astype(x.dtype, copy=False) if len(inputs) == 3: b = inputs[2] y += b return y,
ドット演算(行列の積)している。
あてがうのは、 W.data.T なので、 transpose されたものだ。
検算
y = 2.00304031 x + 1.98421395
に、x=3, 4, 5 を入れてみると、たしかに、 7.99333477, 9.99637508, 11.9994154 になる。
Openai Gym を試す
インストール
$ git clone https://github.com/openai/gym $ cd gym $ sudo pip install -e .
テスト
import gym env = gym.make('CartPole-v0') env.reset() for _ in range(1000): env.render() env.step(env.action_space.sample()) # take a random action
なお、XRDPのリモートデスクトップ環境では再生できませんでした。