その19 に少し手を加え、Ajaxによりページ遷移なしに結果画像を表示できるようにしてみます。
完成形はこちらに置いてみました。自由に動かしてみてください。
http://notiz.flnet.org/CannyWebApp
今回は全部がASP.NET MVCの話で、OpenCvSharpの新しい話はありません。このシリーズに入れて良いのか微妙ですがご容赦ください。
目標
先にも書きましたが、以下のページで完成形を動かしてみることができます。
http://notiz.flnet.org/CannyWebApp
2つのスライダで、Cannyエッジ検出の閾値を自由に変えられます。「Run Canny」を押すと画像が更新されます。
※ヘンな画像を送り付けられると困るので、アップロード機能は無くしています。
※我が家の非力なAtomサーバなので、落ちているかもしれません。ご承知おきください。
ビュー
前回は空テンプレートで作成しましたが、jQueryなどを使いたいので、今回はMVCのテンプレートで始めます。
プロジェクト作成周りは省略します。HomeControllerとIndexのビューがある前提で始めます。
Views/Shared/_Layout.cshtml
MVCでAjaxを使ったり、jQuery UIを使ったりするための準備です。もうちょっと良い書き方がある?
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") <link rel="stylesheet" href="~/Content/themes/base/jquery.ui.all.css"> </head> <body> @Scripts.Render("~/bundles/jquery") @RenderSection("scripts", required: false) <script src="@Url.Content("~/Scripts/jquery-ui-1.8.24.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script> @RenderBody() </body> </html>
Views/Home/Index.cshtml
目標のところで貼った画像のようなビューをつくります。スライダはjQuery UIを使用しています。
@{ ViewBag.Title = "Index"; } <script> $(function () { var initialThreshold1 = 80; var initialThreshold2 = 180; $("#slider1").slider({ min: 0, max: 255, value: initialThreshold1, step: 1, animate: "fast", slide: function (event, ui) { $("#amount1").html(ui.value); $("#threshold1").val(ui.value); //$("#form").submit(); } }).css({ width: "300px", }); $("#slider2").slider({ min: 0, max: 255, value: initialThreshold2, step: 1, animate: "fast", slide: function (event, ui) { $("#amount2").html(ui.value); $("#threshold2").val(ui.value); //$("#form").submit(); } }).css({ width: "300px", }); // 最初の処理 $("#amount1").html(initialThreshold1); $("#amount2").html(initialThreshold2); $("#threshold1").val(initialThreshold1); $("#threshold2").val(initialThreshold2); $("#form").submit(); }); </script> <h1>Index</h1> @using (Ajax.BeginForm("Canny", "Home", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "resultImage"}, new { id = "form" })) { <input type="hidden" id="threshold1" name="threshold1" /> <input type="hidden" id="threshold2" name="threshold2" /> <div style="margin-top: 10px;"> Threshold 1: <span id="amount1" style="font-weight: bold"></span> <div id="slider1"></div> </div> <div style="margin-top: 5px;"> Threshold 2: <span id="amount2" style="font-weight: bold"></span> <div id="slider2"></div> </div> <input type="submit" value="Run Canny" style="margin-top: 15px; font-size:larger; width:256px;" /> } <div id="resultImage" style="margin-top:25px;"></div>
ここのキモはAjax.BeginFormで、これでAjaxによる操作を可能にします。AjaxOptionsのUpdateTargetId で、AjaxによりDOMのどの要素が書き換わるかを指定しています。今回は、一番下の空の<div>を指定してあり、ここに結果画像の<img>タグが入れ込まれるイメージです。
コントローラ
Controllers/HomeController.cs
おおむね前回と似ています。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.UI.WebControls.WebParts; using OpenCvSharp; namespace CannyWebApp.Controllers { public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } [HttpPost] public ActionResult Canny(int threshold1, int threshold2) { if (!Request.IsAjaxRequest()) { return new EmptyResult(); } // 異常な値のリクエストを修正 threshold1 = Math.Min(255, Math.Max(0, threshold1)); threshold2 = Math.Min(255, Math.Max(0, threshold2)); // 用意された画像を読み込み using (var image = new IplImage(Server.MapPath(@"~/Content/Lenna.bmp"), LoadMode.GrayScale)) { // Cannyエッジ検出 using (var cannyImage = new IplImage(image.Size, BitDepth.U8, 1)) { Cv.Canny(image, cannyImage, threshold1, threshold2); // Canny画像をPNGエンコードでバイト配列に変換し、さらにBase64エンコードする byte[] cannyBytes = cannyImage.ToBytes(".png"); string base64 = Convert.ToBase64String(cannyBytes); ViewBag.Base64Image = base64; return PartialView("_CannyImage"); } } } } }
Content/Lenna.bmp は用意して置いてください。Server.MapPathで、動作時には実際の物理パスに置き換えられます。
最も違うのは、PartialViewで返しているところでしょう。これにより、ビューの一部分だけを差し替えるようなことができます。
この時点では_CannyImageビューは無いので文字列が赤くなっていると思います。次はこれを作ります。ビューを作るにあたって必要なデータ、すなわちCannyの結果画像は、ViewBag.Base64Imageに入れてとっておきます。
PartialView
Views/Home/_CannyImage.cshtml
ソリューションエクスプローラのViewsのところで右クリックし、新規作成します。このとき、「部分ビューとして作成する」に必ずチェックを入れてください。(部分ビューとPartialViewがいささか結びつきにくいですね。)
中身はシンプルに、<img>タグ1つ。その19で取った方法同様に、Base64文字列によりオンメモリの画像データを表示します。
@Html.Raw(String.Format("<img src='data:image/png;base64,{0}' />", ViewBag.Base64Image));
これで完成です。こんなふうになるでしょうか。
Index.cshtmlのスライダのコールバックでコメントアウトしているところを有効にすると、スライダ操作の度に画像が更新されるようになります。OpenCVのHighGUIには充分追いついた感じで、ローカルで動かす分には大変面白いです。