2015年9月12日土曜日

YV12To422 その3

YV12To422を更新しました。

YV12To422
YV12To422-1.0.2.zip

PlanarToolsのほうで書いたYV16->YUY2変換がこっちのものよりも速かったので、同じものに変更しました。

PlanarTools その2

PlanarToolsを更新しました。

PlanarTools
PlanarTools-0.2.0.zip

今回はRGBToRGBというフィルタを追加しました。
RGB24<->RGB32を行う、ConvertToRGBのRGB入力専用版です。
名前はPlanarToolsなのにplanar formatは全く関係ありません…どうしてこうなった?

このフィルタはそもそもavisynth+のSSSE3版ConvertToRGBがなんだか妙に遅いような気がしたので、それを検証するために書いたものです。
なんであれは_mm_alignr_epi8(PALIGNR)を使わないで24バイトずつ処理してるんですかね?

結果としては、こっちのコードのほうが倍近く速いようです。
特に工夫らしいこともしていない、安直なコードなんだけどなぁ…。

2015年9月10日木曜日

PlanarTools

avisynth2.6/avisynth+用プラグインをまた書きました。

PlanarTools
PlanarTools-0.0.1.zip

RGBな素材に対してmasktools2とかのplanar format専用フィルタを使うときの補助用フィルタです。

内蔵フィルタを組み合わせても同じことは出来ますが、いちいち関数化したりするのもめんどいし、また内蔵でもShowAlphaとかMargeRGBとかはほぼ最適化なしな状態で遅いので、自分的には需要があります。

今回RGB24<->YV24をSSSE3なしでSIMD化する方法としてこれを使いました。
これを初めて見たときは目から鱗でしたね。
UtVideoの梅澤さんも「まったく同じ操作5回で完了というのは美しい」と言ってました。

うん、お美事にございまする。

追記:
テンプレート使って実行時分岐を減らしたら少し速くなりましたので0.0.1を上げました。
でもバイナリサイズも倍以上に膨れましたが…。

なお配布しているバイナリは/arch:SSE2でビルドされていますが、/arch:AVXにすればVEX-SSEが使われるのでもう少し速くなります。
興味のある人は自分でビルドしてみてください。

2015年9月1日火曜日

Google Maps API V3の叩き方 その2

さて前回書いたコードだが、実は明らかに無駄な部分がある。
無駄を省けばこうなる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
<div id="gmap" style="width:640px;height:480px;margin: 0 auto;"></div>
<script src="//maps.googleapis.com/maps/api/js?sensor=FALSE"></script>
<script>
(function() {
var map = new google.maps.Map(document.getElementById("gmap"), {
  center: new google.maps.LatLng(33.58922, 130.3928),
  zoom: 12,
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

var info = new google.maps.InfoWindow();

function putMarker(p) {
  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(p.lat, p.lng),
    icon: "//chart.apis.google.com/chart?chst=d_map_pin_icon_withshadow&chld=bus|" + p.color,
    title: p.name,
    map: map
  });
  google.maps.event.addListener(marker, "click", function() {
    info.setContent(
      "<p style='font-size:120%;font-weight:bold;'>" + p.name +"</p>" +
      "<p>" + p.addr + "</p>");
    info.open(map, marker);
  });
}

var place = [
  {
    name: "中央区役所", color: "FF0000",
    lat: 33.5892217, lng: 130.3928651,
    addr: "福岡県福岡市中央区大名2丁目5-31",
  }, {
    name: "博多区役所", color: "00FF00",
    lat: 33.5915037, lng:130.4147805,
    addr: "福岡県福岡市博多区博多駅前2丁目9-3",
  }, {
    name: "東区役所", color: "0000FF",
    lat: 33.617762, lng: 130.417401,
    addr: "福岡県福岡市東区箱崎2丁目54?1",
  }, {
    name: "早良区役所", color: "FFFF00",
    lat: 33.5818585, lng: 130.3484381,
    addr: "福岡県福岡市早良区百道2丁目1",
  }, {
    name: "南区役所", color: "00FFFF",
    lat: 33.561562, lng: 130.426442,
    addr: "福岡県福岡市南区塩原3丁目25-1"
  }, {
    name: "西区役所", color: "FF00FF",
    lat: 33.5828543, lng: 130.3232053,
    addr: "福岡県福岡市西区内浜1丁目1-4-1"
  }
];

for (var i = 0; i < place.length; ++i) {
  putMarker(place[i]);
}
})();
</script>
</body>
</html>
情報ウィンドウを複数同時に表示したくないのであれば、最初からInfoWindowは一つだけnewして、イベントのたびにsetContent->openすればいいだけ。
わざわざ現在表示してるオブジェクトを変数に登録したりcloseしたりする必要はない。
調べてた(サンプルになりそうなものを探してた)時になんか違和感を感じてたんだが、原因はこれだったか…。

Google Maps API v3の叩き方

メモ代わり
一番めんどくさいのはピンを刺す場所をgoogle mapで探して緯度と経度を調べることなのは言うまでもない。
Google Maps JavaScript API V3 Reference
dynamic_icons

ところでマーカーにimage Chartsのdynamic icon使えるのはお手軽でいいのですが、image Chartsってdeprecatedってことは廃止予定なんですよね?
mapsのほうにアイコンだけでも持ってきてほしいものですが…

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
<div id="gmap" style="width:640px;height:480px;margin: 0 auto;"></div>
<script src="//maps.googleapis.com/maps/api/js?sensor=FALSE"></script>
<script>
(function() {

function putMarker(p, map, toggleMarker) {
  var info = new google.maps.InfoWindow();
  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(p.lat, p.lng),
    icon: "//chart.apis.google.com/chart?chst=d_map_pin_icon_withshadow&chld=bus|" + p.color,
    title: p.name,
    map: map
  });
  google.maps.event.addListener(marker, "click", function() {
    info.setContent(
      "<p style='font-size:120%;font-weight:bold;'>" + p.name +"</p>" +
      "<p>" + p.addr + "</p>");
    toggleMarker(info, map, marker);
  });
}

var toggleMarker = (function() {
  var current = null;
  return function(info, map, marker) {
    if (current !== null) {
      current.close();
    }
    info.open(map, marker);
    current = info;
  };
})();

var place = [
  {
    name: "中央区役所", color: "FF0000",
    lat: 33.5892217, lng: 130.3928651,
    addr: "福岡県福岡市中央区大名2丁目5-31",
  }, {
    name: "博多区役所", color: "00FF00",
    lat: 33.5915037, lng:130.4147805,
    addr: "福岡県福岡市博多区博多駅前2丁目9-3",
  }, {
    name: "東区役所", color: "0000FF",
    lat: 33.617762, lng: 130.417401,
    addr: "福岡県福岡市東区箱崎2丁目54?1",
  }, {
    name: "早良区役所", color: "FFFF00",
    lat: 33.5818585, lng: 130.3484381,
    addr: "福岡県福岡市早良区百道2丁目1",
  }, {
    name: "南区役所", color: "00FFFF",
    lat: 33.561562, lng: 130.426442,
    addr: "福岡県福岡市南区塩原3丁目25-1"
  }, {
    name: "西区役所", color: "FF00FF",
    lat: 33.5828543, lng: 130.3232053,
    addr: "福岡県福岡市西区内浜1丁目1-4-1"
  }
];

var map = new google.maps.Map(document.getElementById("gmap"), {
  center: new google.maps.LatLng(33.58922, 130.3928),
  zoom: 12,
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

for (var i = 0; i < place.length; ++i) {
  putMarker(place[i], map, toggleMarker);
}
})();
</script>
</body>
</html>


2015年8月29日土曜日

YV12To422 その2

YV12To422を更新しました。

https://github.com/chikuzen/YV12To422

YV12To422-1.0.1.zip

低負荷でそこそこ高速にはなってるので、とりあえずddcc.dllのyv12toyuy2や本体内蔵フィルタのConvertToYUY2の置き換えにはなると思いますが、なにぶん変換のパターンが多い(interlaced(2) x itype(3) x cplace(4) で24パターン + lshift + yuy2)ので確認漏れとかありそうです。

不具合を見つけたら報告おねがいします。


今回cubic補間の実装のため、avisynth+のresamplerのコードを読みましたが、ずいぶんわかりやすくなってますね。
avisynthのインラインASMコードはまったく分からなかったのと比べてすごい違いです。
やってること自体はすごく簡単だったのでちょっとびっくりしました。

追記:
20150831 ちょっと修正したのでバイナリを上げなおしました。

2015年8月22日土曜日

YV12To422

久々にavisynthプラグインを書きました。

https://github.com/chikuzen/YV12To422

YUVTo422-0.0.0.zip

前回のYV12ToYUY2の記事書くときにドキュメントやコードを読んでたらいつの間にやらVisualStudio起動してました。

なんつーか、triticalコードって読んでるうちに自分でやってみたくなるんですよね…。

avisynth+だと、どうもマルチスレッド化されたフィルタはうまく動かないらしいので、シングルスレッドでSIMD使うようにしました。

結果的に出力は同じで、より高速低負荷になってます。

まだまだ未実装の機能もありますが、興味がある方はどうぞ。

追記:
どうやらavisynth+でうまく動かなかったのはたまたまだったらしい。
今ではddcc.dllもちゃんと動いている。
一体なにが原因だったのだろう?

2015年8月19日水曜日

YV12ToYUY2

2chのソフトウェア板見てみたら、YV12->YUY2でちょっともりあがってたようなので久しぶりにエンコネタで書いてみる。

なんでもConvertToYUY2()を色差補間なしの可逆変換したい層がそこそこいるそうで、ddcc.dllのYV12ToYUY2をitype=0で使っているらしい。
で、今回ddcc.dllのコード見てみたのだけど、これってinterlaced=false,cplace=3の時以外は、ddcc.dll使わなくてもよさそう。

とりあえずitype=0の場合はavisynth2.6だとこのスクリプトで代用できる。
(itype=0の場合、cplaceは指定しても無視される)
#YV12ToYUY2_no_interp.avs

function YV12ToYUY2_ni(clip c, bool "interlaced")
{
    is_shit = default(interlaced, false)
    assert(c.IsYV12(), "input clip is not YV12.")
    return is_shit ? c.YV12ToYUY2_ni_i() : c.YV12ToYUY2_ni_p()
}

function YV12ToYUY2_ni_p(clip c)
{
    w = c.width() / 2
    h = c.height()
    u = c.UToY8().PointResize(w, h)
    v = c.VToY8().PointResize(w, h)
    return YToUV(u, v, c).ConvertToYUY2()
}

function YV12ToYUY2_ni_i(clip c)
{
    sep = c.SeparateFields()
    w = sep.width() / 2
    h = sep.height()
    u = sep.UToY8().PointResize(w, h)
    v = sep.VToY8().PointResize(w, h)
    return YToUV(u, v, sep).Weave().ConvertToYUY2()
}

ddcc.dllのyv12toyuy2はフレーム分割方式でマルチスレッド化はされてるけどCのみで書かれていてSIMDは使ってないので、threads=1だと上記のスクリプトのほうが速い。
もともと重い処理ではないのでシングルスレッドでもボトルネックにはならないと思うけど。

ちなみに上記の処理でYUY2にしたものをYV12に戻す場合はこうなる。
function YUY2ToYV12_ni(clip c, bool "interlaced")
{
    is_shit = default(interlaced, false)
    assert(c.IsYUY2(), "input clip is not YUY2.")
    assert(c.height() % 2 == 0, "height must be mod 2.")
    c = c.ConvertToYV16()
    return is_shit ? c.YUY2ToYV12_ni_i() : c.YUY2ToYV12_ni_p()
}

function YUY2ToYV12_ni_p(clip c)
{
    w = c.width() / 2
    h = c.height() / 2
    u = c.UToY8().PointResize(w, h)
    v = c.VToY8().PointResize(w, h)
    return YToUV(u, v, c)
}

function YUY2ToYV12_ni_i(clip c)
{
    sep = c.SeparateFields()
    w = sep.width() / 2
    h = sep.height() / 2
    u = sep.UToY8().PointResize(w, h)
    v = sep.VToY8().PointResize(w, h)
    return YToUV(u, v, sep).Weave()
}

出力に差があるかないか確認したい場合はこんな感じで
LoadPlugin("ddcc.dll")
LoadPlugin("masktools2.dll")
Import("YV12ToYUY2_no_interp.avs")

src = something_yv12_clip
v0 = src.YV12ToYUY2(interlaced=true, itype=0)
v1 = YV12ToYUY2_ni(interlaced=true)
v2 = v1.YUY2ToYV12(interlaced=true)

ShowDiffYUV(v0, v1)
#ShowDiffYUV(src, v2)

function ShowDiffYUV(clip c0, clip c1)
{
    assert(c0.IsYUV() && c1.IsYUV(), "not YUV clip was found")
    c0 = c0.IsYUY2() ? c0.ConvertToYV16() : c0
    c1 = c1.IsYUY2() ? c1.ConvertToYV16() : c1
    cond = c0.PixelType() == c1.PixelType() && c0.width() == c1.width() &&\
           c0.height() == c1.height()
    assert(cond, "It's impossible to compare.")

    return mt_makediff(c0, c1, chroma="process").mt_lut("x 128 == 128 255 ?", chroma="process")
}
もし出力に違いがあれば灰色以外のケバい色になるはずです。

追記:
20150820 default()の引数の順番間違えてたのを修正

2015年8月16日日曜日

canvasでvideoを描画2

ちょっと考えて前回のコードを一部変更してみた。

  vid.autoplay = true;

  vid.addEventListener("loadeddata", function(){  
      vid.pause();  
      draw();  
      button.value = "play";  
    }, false);  

  vid.autoplay = false;
  vid.loop = true;
  vid.addEventListener("loadeddata", function(){
      vid.play();
      setTimeout(function() {
          vid.pause();
          draw();
        }, 25);
      button.value = "play";
    }, false);

とりあえずloadeddataイベント発生後25ミリ秒だけ再生するようにしたところ、Edge/IEでもChromeと同じ挙動になった。
ただこれでもスマホ(Android/Zenfone5)のChromeだとダメ。

スマホのレイテンシ高すぎるんじゃ…

2015年8月15日土曜日

canvasでvideoを描画

html5のcanvasで動画を扱い方を調べつつ書いたもの。
ブラウザの違いでめんどいことは多いがなれればそれほどでもないかも。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>

<body>
<canvas id="cnvs" width="640" height="480"></canvas>
<input id="play-or-pause" type="button" value="loading">
<script>
(function() {
  var canvas = document.getElementById("cnvs");
  var ctx = canvas.getContext("2d");
  var button = document.getElementById("play-or-pause");
  var vid = document.createElement("video");
  vid.autoplay = true;
  vid.loop = true;
  if (vid.canPlayType("video/mp4").length === 0) {
    return false;    
  }
  vid.addEventListener("loadeddata", function(){
      vid.pause();
      draw();
      button.value = "play";
    }, false);
  vid.src = "susie.mp4";

  function draw() {
    for (var dy = -118; dy < canvas.height; dy += 144) {
      for (var dx = -160; dx < canvas.width; dx += 192) {
        ctx.drawImage(vid, dx, dy);
      }
    }
  }

  button.onclick = function() {
    var intervalId = null;
    if (button.value === "pause") {
      clearInterval(intervalId);
      vid.pause();
      button.value = "play";
    } else if (button.value === "play"){
      intervalId = setInterval(draw, 1000 / 30);
      vid.play();
      button.value = "pause";
    }
  };
})();
</script>
</body>
</html>



前回と同じくボタンで再生/一時停止。

手元の環境(Windows10,64bit)で確認したところ、Chrome/Firefoxではこのページを開いた場合、0フレームが表示された状態でpauseがかかるが、Edge/IE11では最初のフレームは表示されず真っ黒(Edge)か透明(IE11)である。
Edgeはwebkitと挙動が違えばバグらしいのでEdgeのはバグなんだろう(多分)。

追記:
スマホ(Android)のChromeで見てみるとEdgeと同じ挙動になった。
ってことはバグにはならない可能性のほうが高いのかな?
ここらへんはっきりさせて欲しい。

canvasで2Dアニメーション

html5のcanvasアニメーションを調べながら書いたコード。
とりあえず画像の表示と移動、回転を試したかっただけ。
手持ちの素材を適当に使ったらかなりくるった出来上がりになってしまったが気にしてはいけない。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>

<body>
<canvas id="cnvs" width="640" height="480"></canvas>
<input id="play-or-pause" type="button" value="loading">
<script>
/*
  background: 背景のURL
  flyingObjects: 移動物のURLの配列
  num: 移動物の数
  fps: フレームレート
  xmax:1フレーム当たりの移動物のx軸方向の最大移動量(px)
  ymax:1フレーム当たりの移動物のy軸方向の最大移動量(px)
  tmax:1フレーム当たりの移動物の最大回転角(deg) 
*/
(function(background, flyingObjects, num, fps, xmax, ymax, tmax) {

  var canvas = document.getElementById("cnvs");
  var ctx = canvas.getContext("2d");
  var button = document.getElementById("play-or-pause");

  const PI2 = Math.PI * 2;

  var loader = (function() {
    var count = 0;
    return function() {
      if (++count > num) {
        button.value = "play";
        draw();  
      }
    }
  })();

  var bg = new Image();
  bg.src = background;
  bg.onload = loader;

  var fo = new Array(num);
  for (var i = 0; i < num; i++) {
    fo[i] = new Image();
    fo[i].params = {
      speedx: Math.random() * 2 * xmax - xmax,
      speedy: Math.random() * 2 * ymax - ymax,
      dstx: Math.random() * canvas.width,
      dsty: Math.random() * canvas.height,
      theta: ((Math.random() * 2 * tmax - tmax) * Math.PI / 180),
      angle: 0,
    };
    fo[i].src = flyingObjects[Math.random() * flyingObjects.length | 0];
    fo[i].onload = loader;
  }

  function draw() {
    ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
    for (var i = 0; i < num; i++) {
      var f = fo[i];
      var p = f.params;
      var cx = f.naturalWidth / 2;
      var cy = f.naturalHeight / 2;

      ctx.save();
      ctx.translate(p.dstx + cx, p.dsty + cy);
      ctx.rotate(p.angle);
      ctx.drawImage(f, -cx, -cy);
      ctx.restore();

      p.dstx += p.speedx;
      p.dsty += p.speedy;
      p.angle += p.theta;

      if (p.dstx > canvas.width) {
        p.dstx = -f.naturalWidth;
      }
      if (p.dstx < -f.naturalWidth) {
        p.dstx = canvas.width - 1;
      }
      if (p.dsty > canvas.height) {
        p.dsty = -f.naturalHeight;
      }
      if (p.dsty < -f.naturalHeight) {
        p.dsty = canvas.height - 1;
      }
      if (p.angle < -PI2) {
        p.angle += PI2;
      }
      if (p.angle > PI2) {
        p.angle -= PI2;
      }
    }
  };

  button.onclick = (function() {
    var intervalId = null;
    var interval = 1000 / fps | 0;
    return function() {
      if (button.value === "pause") {
        clearInterval(intervalId);
        button.value = "play";
      } else if (button.value === "play") {
        intervalId = setInterval(draw, interval);
        button.value = "pause";
      }
    };
  })();

})("images/soccer.jpg",
   ["images/saturn.png",
    "images/pig.png",
    "images/heart.png",
    "images/japan.gif"],
    50, 50, 3, 5, 30);
</script>
</body>
</html>




ボタンを押せば再生/一時停止します。

2015年8月7日金曜日

PHPでenum(のようなもの)

PHPでファイル送信機能付きのメールフォームを書いていた時のお話し。

入力フォームなんだから当然validationとかもしなければいけないわけで、いろいろやるクラスを別に作って結果を定数で返すようにした。
こういうときCだったらenumを使うのが多分普通なのですが、PHP5にはenumはないんですな。

enumよりももっとスマートな方法もあるのかもしれないけど、パッとは思いつかない。
それにJavaでもC#でも、switch文は頑なに拒否するPythonですらenumは標準モジュールで用意されてるんですから、やはりenumはいいものなのでしょう。

ググってみたら「enum実装しました!」とかいう人も当然いるわけですが、しかしenum程度でわざわざ外部ライブラリとか大げさな気もする。
で、自分なりの方法を模索するわけです。

とりあえずぱっと思いつくのはこれですな。
class RetCode {

    const VALID_POST = 0;
    const INVALID_POST = 1;
    const SHORTAGE_POST = 2;
    const INVALID_FILE_UPLOADED = 3;
      ・
      ・
      ・ 
}

自分で連番ふって定数定義…はい、こんなもんenumじゃありません。
自動で重複しない数値を振ってくれなければenumの替わりとは絶対認められないに決まってます。

で、PHPのドキュメント5分ほど眺めてこうすることにしました。

class RetCode {

    private static $ret = [
        "VALID_POST",
        "INVALID_POST",
        "SHORTAGE_POST",
        "INVALID_FILE_UPLOADED",
            ・
            ・
            ・
    ];

    public static function get_retcode() {
        return array_flip(self::$ret);
    }
}

keyなしで配列定義してarray_flip()してやれば、重複しない値をもつ配列になりますね。
定義されてないkeyを使おうとすればNotice Errorでるから、typo防止にもなるっちゃなりますし。

Cのenumとそれほど手間も変わらないんで、お手軽につかう分にはいいんじゃないかと思います。

2015年8月6日木曜日

JavaScriptで要素の高さを揃える

随分長いことほっぽってたブログですが、何か書きたくなってきたので再開。

といっても、最近は動画関連のことはほとんどやってないので、そっちのほうの需要(あるのか?)は満たせないと思いますが。

で、最近の筆者は普段はhtmlとかJavaScriptとかPHPとかを書いたり書かなかったりしています。
今回のお題はそちらの方面です。

本題

さて、今回のネタは次のような感じです。


こんな感じの表示を
こうしたい。

「要素 高さ 揃える」とかでググればjQuery使ったやり方がたくさんヒットするわけですが、IE8のサポートも残り半年を切ってる昨今では、別にアニメーションさせるわけでもないのにjQuery使うのってダサいらしいので今回はパス。

jQueryを使わずにこれをやるheightLine.jsなるライブラリもあるようですが、実際に中身を見てみたところどうもよく理解できない。書かれたのが8年前ではブラウザ環境とかもずいぶん違うだろうしなあ。

というわけで、自分で書いてみることにしました。


まず高さを揃えたい各要素(element)の処理前の高さは、element.offsetHeightの値でわかるので、各要素のoffsetHeightの最大値を求める。
var elems = document.getElementsByClassName(className); //とりあえずクラス名で対象要素を取得
var maxHeight = 0;
for (var i = 0; i < elems.length; i++) {
    maxHeight = Math.max(maxHeight, elems[i].offsetHeight);
}
これで揃える高さが求まりました。

で、offsetHeightは要素のpadding(上下)+border(上下)+heightなので、maxHeightからpaddingとborderの値を引いた値を要素のstyle.heightに設定してやればいいわけですが、ここで少し問題。

要素のpaddingやborderを<style>タグや外部CSSファイルで指定している場合は、element.style以下のプロパティは全部空("")なのですね。

かと言って高さを揃えたい各要素のborder+paddingは(上の画像のように)すべて同じとは限らないわけで、ハードコーディングするのも問題ありすぎです。

さてどーしようとちょっと考えたらこうなりました。
for (i = 0; i < elems.length; i++) {
    var elem = elems[i];
    elem.style.height = "0";
    var height = maxHeight - elem.offsetHeight;
    elem.style.height = height + "px";
}
offsetHeight==padding(上下)+border(上下)+height ですから、height==0のときoffsetHeight==padding(上下)+border(上下)になる。

これでstyle.heightに入れる値はmaxHeightとoffsetHeightの差をとればよいことになります。 なんかインチキ臭いけど、簡単だし実用上は問題ない(多分)。

実際に使うときはこんな感じになります。