バナナでもわかる話

開設当初は計量経済学・統計学が専門の大学院生でした。今はデータを扱うお仕事をしています。統計学・経済学・投資理論・マーケティング等々に関する勉強・解説ブログ。ときどき趣味も。極力数式は使わずイメージで説明出来るよう心掛けていますが、時々暴走します。

画像認識始めました~Rでスクラッチでエッジ検出をやってみた~

ここ最近画像認識を勉強中です。
とりあえず読んだ傍からスクラッチで書いていっています!
参考本はこれ。

そして過去記事一覧はこちら
画像認識 カテゴリーの記事一覧 - バナナでもわかる話



前回はバイラテラルフィルタをやってみました。


今回はバイラテラルフィルタを使ってノイズを取った画像に対して、エッジ検出というものを行ってみます!

スポンサーリンク



エッジ検出

物体と物体の間の縁(エッジ)を検出する技術をエッジ検出と呼びます!
基本的には画像における画素差の急激な変化であったり輝度の急激な変化が起こった場所を物体の縁だと判断しています!
ただ、これもかなりノイズに引っ張られるので、前回前々回とやったフィルタリング技術を使ったり、検出時点でノイズを考慮したモデルを使う必要があるわけですね。

元画像

ちなみに無加工の元画像はこれです
f:id:bananarian:20181120002450p:plain

で、パイラテラルフィルタをかけたものがこれです。
f:id:bananarian:20181123081624p:plain

前者よりも後者の方がノイズが減っているのが分かりますね。後者を使っていきます。


微分

画素変化は微分で判断します。
ただ、画素値は離散個の点なので、よくある連続関数に対する微分は使えません。
ただ、要は1個ずれた時の差を検出すれば良いわけなので横方向、縦方向に関して差を取ってやれば良いということになります。

微分フィルタ

とりあえずx方向に関して微分するフィルタを用意してやります。

#x方向に対する微分フィルタFを作る
Fx=(1/2)*cbind(c(0,-1,0),rep(0,3),c(0,1,0))
F=array(Fx,dim=c(3,3,3))
#適用してみる
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image6[i,j,]=apply((F*zzz),3,sum)
	}
}

出力結果
f:id:bananarian:20181123085938p:plain

パソコンで見るとうっすら白い線でエッジが見えるのですが、アップしたらただ真っ黒な画像になってますね(笑)
画像のノイズがまだまだ多いので、ほぼエッジが検出出来ていないことによります。そこで、ノイズを考慮した手法を次に紹介します。


ちなみにy方向への微分はこんな感じ

Fy=(1/2)*cbind(rep(0,3),c(-1,0,1),rep(0,3))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image6[i,j,]=apply((F*zzz),3,sum)
	}
}

ノイズも考慮してエッジを検出する方法にプリューウィッドフィルタがあります。

プリューウィッドフィルタ

周りの画素の微分も考慮して周りのノイズの影響を減らそうという発想で作ったのがこれです。

#プリューウィッドフィルタ
image7=array(0,dim=c(dim(image)[1],dim(image)[2],3))
#x方向への微分
Fx=cbind(rep(-1,3),rep(0,3),rep(1,3))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image7[i,j,]=apply((F*zzz),3,sum)
	}
}

出力結果
f:id:bananarian:20181123100443p:plain


さっきよりも縁が浮き出てますね。手の形とかもわかるようになってます。
実際の画像だともっとキレイに縁検出出来てるんですけど、はてなブログに投稿すると見えにくくなりますね~。
投稿される時に圧縮されてるんでしょうか。

ちなみにy方向へのフィルタはこんな感じ

Fy=cbind(c(-1,0,1),c(-1,0,1),c(-1,0,1))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image7[i,j,]=apply((F*zzz),3,sum)
	}
}

スポンサーリンク



ソーベルフィルタ

プリューウィッドフィルタだと、周りと中心が均等に重みづけされて微分しているわけですが、当然中心が一番重要なのは間違いないので、中心に多めに重みを付けたフィルタがソーベルフィルタです。

コードを見てもらうとわかる通り、真ん中の部分が1じゃなくて2になってますね。

image8=array(0,dim=c(dim(image)[1],dim(image)[2],3))
#x方向への微分
Fx=cbind(c(-1,-2,-1),rep(0,3),c(1,2,1))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image8[i,j,]=apply((F*zzz),3,sum)
	}
}

出力結果
f:id:bananarian:20181123101410p:plain

プリューウィッドフィルタよりも縁が濃く検出されていますね。

ちなみにy方向はこんな感じ

Fy=cbind(c(-1,0,1),c(-2,0,2),c(-1,0,1))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image8[i,j,]=apply((F*zzz),3,sum)
	}
}

ラプラシアンフィルタ

ここまでは1階微分の話でしたが、二階微分して輝度で縁を検出することも出来ます。

image9=array(0,dim=c(dim(image)[1],dim(image)[2],3))
F0=cbind(c(0,1,0),c(1,-4,1),c(0,1,0))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image9[i,j,]=apply((F*zzz),3,sum)
	}
}

出力結果
f:id:bananarian:20181123102821p:plain

良い感じですね~

斜めも考慮したモデルを考えることも出来ます。

image9=array(0,dim=c(dim(image)[1],dim(image)[2],3))
F0=cbind(c(1,1,1),c(1,-8,1),c(1,1,1))
F=array(Fx,dim=c(3,3,3))
for(i in 2:(dim(image)[1]-1)){
	for(j in 2:(dim(image)[2]-1)){
		zzz=image5[max(1,(i-1)):min(dim(image)[1],(i+1)),max(1,(j-1)):min(dim(image)[2],(j+1)),]
		image9[i,j,]=apply((F*zzz),3,sum)
	}
}

f:id:bananarian:20181123103546p:plain


こんな感じで、画像の中にある物体のエッジを検出することが出来るわけですね。