Help us understand the problem. What is going on with this article?

【JS】360°パノラマ写真からCanvasを使って惑星「リトルプラネット」を作る

More than 3 years have passed since last update.

概要

「リトルプラネット(Little Planet)」というのをご存知でしょうか?
こういう写真です。

stock-photo-beautiful-small-green-village-from-above-aerial-view-little-planet-concept-115348069.jpg

ドラゴンボールの界王星みたいな感じのやつですね。

最近THETAが普及して、気軽に360°撮影が可能になりSNSで投稿する機会が増えたことから、こういうキレイな写真を見かけたことがある方は多いと思います。

実はこれ、特別な画像変換ソフトウェアを使わなくてもTHETA等で撮った360°パノラマ写真さえあれば、canvasタグとJavaScriptだけで、Little Planetに加工することができます。
もちろんPhotoShopでも加工が可能です。
加工の仕方さえわかってしまえば、様々な場面で応用ができるでしょう。

自分も最初はどういう加工を施せばいいのかわかりませんでしたが、

Webクリエイターボックスさんの以下の記事で、意外に簡単であることがわかり、ブラウザ上で加工・変換することを思いつきました。

作り方

使う素材はこちら

test.jpg

チャリンコに乗りながら公園で適当に撮った一枚です。
この正距円筒方式の写真をLittle Planetにしてみました。

canvasでどういう処理を行えばいいのかというと、それは至ってシンプルで
以下の3つのポイントだけおさえておけば大丈夫です。

  • 写真の幅と高さを同じサイズで正方形にする
  • 写真を180°反転させる

スクリーンショット 2015-12-28 1.44.22.png

これが正方形に変形させて、反転した状態です。
そして、、

  • canvasを極座標に変換する

極座標」って何?って話ですが、、

通常、x軸とy軸で交わる部分を点であらわすことが多いと思いますが、これは直交座標というもので、

これに対し極座標と言うのは原点からの方向(角度)と距離で位置を表します。

Untitled.png

世界地図で言えば、南極点を中心に円状に広がっていくあの地図です。

電測(レーダー)にも使われてますね。

プログラムで画像を処理

以下使用したコードの詳細です。

Canvasを極座標に変換する方法についてはこちらのサイトを参考にしました。というより一部コードを引用させていただきました。

参考先でも述べられているように、Canvasを極座標に変換する適当な関数が無いので自前で作る必要があります。

変換元のフィールドに描画して、そこからgetImageDataで取得して、変換された形で再描画しています。

js


var WIDTH = 500;
var HEIGHT = 500;
var img = new Image();
img.src = "test.jpg";
var rotDig = 0;

window.onload = function (){
    test();
}

function test() {

    var canvas = document.getElementById('canvas');

    var hw = WIDTH / 2;
    var hh = HEIGHT / 2;
    var r = Math.sqrt(hw * hw + hh * hh);
    var lut = [];
    var pos = 0;

    for (var y = 0; y < HEIGHT; y++){
        for (var x = 0; x < WIDTH; x++){
            var sx = Math.atan2(y - hh, x - hw) * hw / Math.PI + hw;
            var sy = Math.sqrt((x - hw) * (x - hw) + (y - hh) * (y - hh));
            sy = (r - sy) / r;
            sy = HEIGHT - sy * sy * HEIGHT - 1;

            if (sx < 0) sx = 0;
            if (WIDTH - 1 < sx) sx = WIDTH - 1;
            if (sy < 0) sy = 0;
            if (HEIGHT - 1 < sy) sy = HEIGHT - 1;

            sx = Math.round(sx);
            sy = Math.round(sy);

            lut[pos++] = (sy * WIDTH + sx) * 4 + 0;
            lut[pos++] = (sy * WIDTH + sx) * 4 + 1;
            lut[pos++] = (sy * WIDTH + sx) * 4 + 2;
            lut[pos++] = (sy * WIDTH + sx) * 4 + 3;
        }
    }

    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
        var angle = 180*Math.PI/180;
        ctx.rotate(angle);
        ctx.translate(-WIDTH, -HEIGHT);
        ctx.drawImage(img, 0, 0, WIDTH, HEIGHT);

        var output = ctx.getImageData(0, 0, WIDTH, HEIGHT);

        var imageData = ctx.getImageData(0, 0, WIDTH, HEIGHT);
        var src = imageData.data;
        var dst = output.data;
        for (var n = 0; n < HEIGHT * WIDTH * 4; n++){
            dst[n] = src[lut[n]];
        }
        ctx.putImageData(output, 0, 0);
    }

}


完成形

スクリーンショット 2015-12-28 0.41.12.png

意外にきれいに仕上がります!

最後に

これ、、、、恐らくvideoタグを読み込めば同じ方法でできると思うので、また時間あるときにやってみます。

360°パノラマはやはりLittle Planetに加工するのがやはり一番きれい!
なんで、これからどんどん撮影して惑星を作っていきたいと思います。

参考

kingpanda
キングパンダ合同会社 代表。SEOやコンテンツマーケティングなどSEOに関する記事やその他、技術的情報を記事として更新します。各種お問い合わせもTwitter DMなどからお気軽にどうぞ! 採用動画プラットフォーム「ENLIST(エンリスト)」を運営
https://enlist.jp/
king_panda
採用動画プラットフォーム「ENLIST」を運営・開発しています。https://kingpanda.jp
https://kingpanda.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away