コンピュータビジョンのライブラリOpenCVのアドベントカレンダーです。OpenCVの知られざる機能(マイナーとも言う)、こんなプラットフォームでOpenCVを動かした、分かりづらいバグに遭遇した、こんな便利な機能があるなどの情報を中心に書いてます。http://qiita.com/advent-calendar/2015/opencv (昨年のAdvent Calendar)あと、今年は昨年と違い執筆希望者多数で、以下の記事もAdvent Cale... OpenCV Advent Calendar 2016 - Qiita - Qiita |
image_match - Check web page design using image file automatically zuqqhi2/image_match - GitHub |
このコードは以下の流れで処理されます。## # # Calculate matching score of 1st input image and 2nd input image. # The 2nd input image size must be smaller than 1st input image. # This function is robust for brightness. # # @param [String] scene_filename Scene image file path # @param [String] template_filename template image file path which you want find in scene image # @param [Float] limit_similarity Accepting similarity (default is 90% matching) # @param [Boolean] is_output if you set true, you can get match result with image (default is false) # @return [Boolean] true if matching score is higher than limit_similarity # false otherwise # def perfect_match_template(scene_filename, template_filename, limit_similarity=0.9, is_output=false) raise ArgumentError, 'File does not exists.' unless File.exist?(scene_filename) and File.exist?(template_filename) raise ArgumentError, 'limit_similarity must be 0.1 - 1.0.' unless limit_similarity >= 0.1 and limit_similarity <= 1.0 raise ArgumentError, 'is_output must be true or false.' unless is_output == false or is_output == true scene, template = nil, nil begin scene = IplImage.load(scene_filename) template = IplImage.load(template_filename) rescue raise RuntimeError, 'Couldn\'t read image files correctly' return false end return false unless scene.width >= template.width and scene.height >= template.height result = scene.match_template(template, :ccoeff_normed) min_score, max_score, min_point, max_point = result.min_max_loc if is_output from = max_point to = CvPoint.new(from.x + template.width, from.y + template.height) scene.rectangle!(from, to, :color => CvColor::Red, :thickness => 3) scene.save_image(Time.now.to_i.to_s + "_match_result.png") end return (max_score >= limit_similarity ? true : false) end
そして以下のようなコードを書きます。source 'https://rubygems.org' gem 'capybara', '~> 2.1' gem 'poltergeist', '1.12.0' gem 'image_match', '0.0.3'
後半の数行以外は、Capybaraとpoltergeistというライブラリを使ってphantomjsというヘッドレスブラウザで、実際にサイトにアクセスしてスクリーンショットを取ってきているだけです。 そして、テンプレート画像と類似度0.9以上で類似する箇所が見つかった場合に、”Find!”と標準出力に出力しています。 この時点でディレクトリ構成はrequire 'capybara' require 'capybara/poltergeist' require 'image_match' include ImageMatch # Get current google web page image url = 'http://160.16.71.152:12080/' Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new app, js_errors: false end Capybara.default_selector = :xpath session = Capybara::Session.new(:poltergeist) session.driver.headers = {'User-Agent' => "Mozilla/5.0 (Macintosh; Intel Mac OS X)"} session.visit(url) session.save_screenshot('screenshot.png', full: true) # Compare logo (output match result image) # When you write perfect_match_template('./screenshot.png', './template.jpg'), nothing outputting result image if perfect_match_template('./screenshot.png', './template.jpg', 0.9, true) puts "Find!" else puts "Nothing..." end
となっていて、実行するには以下のコマンドを叩きます。. ├── Gemfile ├── template.jpg └── sample.rb
実行結果は以下のようになりました(実際に実行すると”Unsafe JavaScript attempt…”というメッセージが大量に出ますが無視してください。私のサイトが悪いだけです。)。 limit_similarity を調整すればテンプレート画像が色違い程度であれば認識してくれます。 ちなみに、perfect_matchの方は、テンプレート画像が一部ではなくサイトのスクリーンショットと同じサイズの場合の関数です。bundle install --path=.bundle bundle exec ruby main.rb # Output > Find!
MIRU2013のチュートリアル「画像局所特徴量SIFTとそれ以降のアプローチ」 第16回画像の認識・理解シンポジウム MIRU2013 2013年7月29日 http://cvim.ipsj.or.jp/miru2013/tutorial.php#ts4 MIRU2013チュートリアル:SIFTとそれ以降のアプローチ - |
fuzzy_match_templateは単純に特徴量同士で、探索される画像とテンプレート画像を比較しています。 match_template_ignore_sizeはかなり力技なことをやっていて、以下の流れで処理を行います。param = CvSURFParams.new(500) template_keypoints, template_descriptors = template.extract_surf(param)
get_object_location関数で探索される画像とテンプレート画像の特徴量の算出、それをもとに対応する特徴点同士を見つけてテンプレート画像の位置を特定します。この段階で終了するとfuzzy_match_template関数と同じです。回転や歪みに強い性質をあえて打ち消すために、最後にテンプレートマッチングをしています。 以下でmatch_template_ignore_sizeを試してみます。 適当に先程のちょうちょの画像を75%のサイズに縮小します。 この画像を使って、先程と同じスクリプトを実行すると”Nothing…”と出力され、明確な誤りではないので例があまり良くないのですが、一番近いところは以下のように先程とくらべて若干ずれてます。 ちなみに、limit_similarityを0.4にしても”Nothing…”になります。 match_template_ignore_sizeにすると0.6で”Find!”となり(拡大してマッチングしているだけなので現状だと類似度はかなり下がってしまいます)、一番近いところは以下のように先程とあまり変わらない位置と大きさになっています。 コードは先程のコードから以下ように1行差し替えるだけです。## # # Calculate matching score of 1st input image and 2nd input image. # The 2nd input image size must be smaller than 1st input image. # This function is robust for brightness and size. # # @param [String] scene_filename Scene image file path # @param [String] template_filename template image file path which you want find in scene image # @param [Float] limit_similarity Accepting similarity (default is 90% matching) # @param [Boolean] is_output if you set true, you can get match result with image (default is false) # @return [Boolean] true if matching score is higher than limit_similarity # false otherwise # def match_template_ignore_size(scene_filename, template_filename, limit_similarity=0.9, is_output=false) raise ArgumentError, 'File does not exists.' unless File.exist?(scene_filename) and File.exist?(template_filename) raise ArgumentError, 'is_output must be true or false.' unless is_output == false or is_output == true dst_corners = get_object_location(scene_filename, template_filename) scene, template = nil, nil begin scene = IplImage.load(scene_filename) template = IplImage.load(template_filename) rescue raise RuntimeError, 'Couldn\'t read image files correctly' return false end return false unless scene.width >= template.width and scene.height >= template.height if dst_corners src_corners = [CvPoint.new(0, 0), CvPoint.new(template.width, 0), CvPoint.new(template.width, template.height), CvPoint.new(0, template.height)] resize_width = (dst_corners[1].x - dst_corners[0].x) - src_corners[1].x resize_height = (dst_corners[3].y - dst_corners[0].y) - src_corners[3].y template = template.resize(CvSize.new(template.width + resize_width, template.height + resize_height)) end result = scene.match_template(template, :ccoeff_normed) min_score, max_score, min_point, max_point = result.min_max_loc if is_output from = max_point to = CvPoint.new(from.x + template.width, from.y + template.height) scene.rectangle!(from, to, :color => CvColor::Red, :thickness => 3) scene.save_image(Time.now.to_i.to_s + "_match_result.png") end return (max_score >= limit_similarity ? true : false) end
これで、後はテストコード化して、JenkinsなどのCI環境でテストを自動的に実行させる設定を入れるだけで、ある程度デザインの目視チェックを自動する仕組みが出来ます。 また、この記事では簡単のためにCapybara+poltergeistを使いましたが、SeleniumRCを使えばFirefoxやChromeなどを動かせるので、特定のブラウザでのみデザインが崩れる場合にも対処できます。# from #if perfect_match_template('./screenshot.png', './butterfly-small.jpg', 0.4, true) # to if match_template_ignore_size('./screenshot.png', './butterfly-small.jpg', 0.6, true)
Webdrvier Remoteを利用してマルチブラウザテストを実施 Webdriver RemoteでFirefox,IE,Chrome上で自動ブラウザテスト - ズッキーニのプログラミング実験場 |