Home

アイエスエス株式会社 "Innovative System Solutions"

画像から対象を検出して数える検討

上司「同じ物が複数写っている画像に対して,ユーザがその中の1個を『これ』って指定したら残りのやつを全部見つけるソフトの検討しろ」
私「条件が自由すぎて難し…
上司「いいからやれ.これ画像例な.」

という話になっており,泣きそうです.
例として渡されたが画像はコレ↓
Mame
どうやら目的は,こういった画像上で対称物体の個数をカウントしたいという感じのようです.
(撮影対象は南瓜の種の表面に謎の粉が付着したMADE IN CHINAな菓子(?)ですが,面倒なので本稿ではこれを単に「豆」と呼称することにします.)

与えられる情報は正例1個だけなので機械学習みたいな方法はとれません,というかそもそも特定の対象(例えば「人」とか「車」とか)を検出するという話ではないために,対象物に対する前提知識が全く使えません.
指定された領域をテンプレートとしたテンプレートマッチングくらいしか方法が思い浮かばないので,そういった方向で検討開始.

【検討内容】
画像上を走査し,各位置でのZernikeモーメント特徴と正解領域の同特徴とを比較してそれっぽいところを見つける.

なんでZernikeモーメント?→特徴量が回転不変とのことなので,豆がいろんな方向を向いて写っていることに対処してくれるかな?ということで採用.

豆のサイズにはいくらか変動があるので,走査窓のスケールを変えたりする必要がありそうですが,その辺のことは後回しにして,まずは単一のスケールだけで走査してみました.
特徴量比較結果がどの程度であれば「それっぽい」のか?という判断基準が必要ですが,適当に結果値を閾値で切るのではあまりうまくいかなかったので,正解画像のモーメントを元にしてそこから「明らかに不正解っぽい」モーメントを複数作って(=でっちあげて),「それらよりも正解の方に近いこと」という条件で判定してみました.
また,比較に際しては,n=m=0の要素は用いないことにしました.
太い赤丸が指定された正解領域で,細いのが検出結果です.
Mame1Sel
この例では,半径方向の次数(? 正式には何と呼ぶのか不明)nは n=0~6まで(かつ半径方向の次数(?)m≧0 を,n+m≦6 に制限)のZernike多項式を用いて特徴ベクトルを計算しています.
赤丸が豆の中央をきちんと捉えていないものが多く,「見つけた」と言っていいのかどうか判断しかねる結果.
(複数の走査スケールで処理すれば改善するだろうか?)
あと,Zernikeモーメントからの特徴量は画像輝度を反転しても変わらないようで,最初何も考えないでやったら「中央が背景で周辺部が豆」な個所が大量に出てきてしまい途方にくれましたが,そのような個所についてはn=2,m=0のモーメントの符号で判断して除外することにしました.

ちなみに,正解をもう一つ指定して,2回の処理の結果を統合してみると↓のようになります.
2回目の指定領域が緑太丸で,検出結果が緑細丸です.
ただし,2回の処理結果の中で位置が重複したものについては,より良さそうな方を残すことにし,除外されたものは青で描画してあります.
Meme2Sel

正解画像のモーメントから画像を再構成してみるとこんな感じ↓(画像は2倍拡大してある.1回目のが左,2回目のが右)
MameRecon
かろうじて正円ではなくて楕円な雰囲気が感じ取れますが,豆なのか何なのかわからない様相となっています.
より次数の高い項までを用いればそれだけ高周波成分の情報が増えるので豆な形に近づくのですが,やりすぎると今度は豆のテクスチャ(模様)の差が大きく影響するようになってくるので,増やせばいいというわけでもなさそうで,この辺の決め方をどうするのかについては難しい問題に思います.

他の画像(ネットで拾ってきた猫画像)での例
Nekos
やはりなんとも言い難い結果です.

あと,どうでもいい話ですが,この豆画像,グレースケールで見るとMy Favorite菓子である みながわ製菓のとうがらしの種 にそっくり.
(売っている店が近場にほとんど無くて困っています! オンラインショップで買えるけど,価格に占める送料の割合が大きすぎるのがどうにも…)

by nakiusagi3

Retinexをpython+OpenCVでお手軽に味見する

認識処理の前処理でSingle scale retinexをチョットかけてみたいなあというときにお手軽にPythonで実装してみようというお話です.
私が使っているのは Python2.7.1(Windows) + OpenCV2.3.1です.
OpenCV2.3系になってPythonでの使い勝手が更に向上したと感じています.
画像も行列もNumpyのarrayで統一されているので,あれもこれも,気持よく処理できます.
ますますCやC++を使うのが億劫になりそうです.
Retinexについては,ネット上に詳しい資料がたくさんあるので説明は省略させて頂きますが,恥ずかしながら私の書いたPythonスクリプトを参考用に載せておきます.
このスクリプトで処理した画像の例は次の通りです.

原画像

原画像


Retinex処理後

Retinex処理後


左側にいる歩行者がなんとなく確認しやすくなったかもしれません...
本物はGaussian使うべきなのでしょうが,認識の前処理ならBlurでも問題なかったりしますよね.
pythonスクリプトのコメントアウトを外せば確認できます.
スピード命のご時世ですので,処理内容はいつも手抜きと骨抜きの方向でございます.

#-------------------------------------------------------------------------------
# Name:        Retinex Test
# Author:      Tomoya Kamata (kamatomo@japan-iss.co.jp)
# Created:     26/12/2011
# Copyright:   (c) 2011 ISS Inc.
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import cv2
import numpy as np
from math import *

def main():
    cv2.namedWindow('source')
    cv2.namedWindow('result')
    img = cv2.imread("source.jpg")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    g1 = (gray + 1.0) / 256.0
    g2 = (cv2.GaussianBlur(gray, (201, 201), 100) + 1.0) / 257.0
    #g2 = (cv2.blur(gray, (51, 51)) + 1.0) / 256.0  #こっちのほうが軽い
    g3 = cv2.log(g1 / g2) #Retinecs

    #正規化してみる
    g3_min, g3_max = g3.min(), g3.max()
    g4 =  ((g3 - g3_min) / (g3_max - g3_min)) * 255.0

    #uint8にキャスト
    result = np.zeros(g4.shape, dtype=np.uint8)
    result += g4

    #画像を保存するならコメントアウト
    #cv2.imwrite("gray.jpg", gray)
    #cv2.imwrite("retinex_out.jpg", result)

    cv2.imshow("source", gray)
    cv2.imshow("result", result)

    cv2.waitKey(-1)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

T.Kamata

Home

Search
Feeds
Authorized
奨学金支援制度
Meta

Return to page top