2015年9月12日土曜日
PlanarTools その2
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
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
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
https://github.com/chikuzen/YV12To422
YUVTo422-0.0.0.zip
前回のYV12ToYUY2の記事書くときにドキュメントやコードを読んでたらいつの間にやらVisualStudio起動してました。
なんつーか、triticalコードって読んでるうちに自分でやってみたくなるんですよね…。
avisynth+だと、どうもマルチスレッド化されたフィルタはうまく動かないらしいので、シングルスレッドでSIMD使うようにしました。
結果的に出力は同じで、より高速低負荷になってます。
まだまだ未実装の機能もありますが、興味がある方はどうぞ。
追記:
どうやらavisynth+でうまく動かなかったのはたまたまだったらしい。
今ではddcc.dllもちゃんと動いている。
一体なにが原因だったのだろう?
2015年8月19日水曜日
YV12ToYUY2
なんでも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を描画
ブラウザの違いでめんどいことは多いがなれればそれほどでもないかも。
<!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アニメーション
とりあえず画像の表示と移動、回転を試したかっただけ。
手持ちの素材を適当に使ったらかなりくるった出来上がりになってしまったが気にしてはいけない。
<!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(のようなもの)
入力フォームなんだから当然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年前ではブラウザ環境とかもずいぶん違うだろうしなあ。
というわけで、自分で書いてみることにしました。
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の差をとればよいことになります。 なんかインチキ臭いけど、簡単だし実用上は問題ない(多分)。