Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

githubリポジトリ内のサブディレクトリのみをダウンロードしてくる方法

今回はgithub上に上がっているリポジトリ内のサブディレクトリのみを取得する方法についてまとめたいと思います。

最近参考にしたいコードをローカルにダウンロードしようと思ったのですが、
リポジトリがとても大きかったので欲しいコードだけ持ってこれないか?と思い、
調べたところ、方法があったのでその備忘録になります。


■前準備

まずダウンロードを行うためのディレクトリを作成していきます。

mkdir TestDir #適当なディレクトリ名
cd ./TestDir
git init
git remote add origin https://hogehoge~~ #リポジトリの設定を行う

■sparsecheckoutを用いてサブディレクトリのみを取得する。

リポジトリ内のサブディレクトリのみを取得するにあたり、
”sparsecheckout”というコマンドを用います。
sparsecheckoutを用いることにより、gitリポジトリの一部のみを取得できるようになります。
※sparseとは"わずかな"という意味だそうです。

まずはsparsecheckoutの設定をtrueにしていきます。

git config core.sparsecheckout true

次に取得したいサブディレクトリをsparse-checkoutに設定していきます。

echo サブディレクトリパス > .git/info/sparse-checkout

ここで、設定するサブディレクトリパスですが、
リポジトリをトップディレクトリとした時のパスを入力すればよいです。
例えば、こちらのリポジトリのsampleのみを取得したい場合には、
https://github.com/Elsammit/ScreenCapture

下記の通りコマンド実行すればOK。

echo sample > .git/info/sparse-checkout

もしくは、

vim .git/info/sparse-checkout

でファイル編集にてサブディレクトリを記載してもOK。

ダウンロードするサブディレクトリは複数選択可能で、
2つ目以降は、

echo サブディレクトリパス >> .git/info/sparse-checkout

とsparse-checkoutに追記する形でコマンド実行すればOK。
vimでファイル内に追記していくでもOKです。

最後に、

git pull origin main #ブランチ名は適宜変更

を実行してダウンロードすればOK。

■補足

sparse-checkoutでダウンロードするファイルを除外する際には、

vim .git/info/sparse-checkout

としてファイル編集にて、

!サブディレクトリ名

と頭にビックリマークを付与すればOK。

■最後に

今回はgithubリポジトリの一部サブディレクトリのみをダウンロードする方法についてまとめました。
どうやらgit上にpushされた情報は全て取り込んだ上でサブディレクトリのみをpullしているようです。
このため、それなりに更新頻度の高いリポジトリはpull時にダウンロード時間が少し長いようです。



OpenCVで作成した文字や四角形を移動させてみる

OpenCVで動画を再生させたり、動画のフレーム数を変えたり、フレームレート切り替えたり、、、
動画を切った貼ったやっていると、
あれ??今フレームレート想定通りになっているかな??遅くない??早くない??
と不安になることがあります。

動画を見比べて判断すればよいのですが、
ただの動画だと分かりにくい。。

ということで、自作でサンプル動画を作成してみることにしました。
ただ連番画像を動画にするだけだと画像作成面倒ですし、やりなれた方法ですので、
今回は、OpenCVのライブラリとして提供されている、
rectangleとTextを用いて、自作で動画を作成してみることにしました。



■作成した動画

今回自作できた動画はこちら。

ただ0~9までの数字を動かしているだけです。
フレームレートは10fpsで録画したものです。

■環境

今回は、Pythonを用いました。
Pythonのバージョンですが、
Python 3.7.4
です。

■rectangle、putText関数の仕様

冒頭でも述べた通り、OpenCVのライブラリを用いて
長方形や文字を入れ込んでいきます。

OpenCVでは長方形や文字を入れるための関数として、
rectangle、putText関数があります。

まずrectangleですが、枠線や塗りつぶした矩形、長方形を描画できます。
こちらのように定義されており、
rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
引数は、
 img – 画像.
 pt1 – 矩形の1つの頂点.
 pt2 – pt1 の反対側にある矩形の頂点.
 color – 矩形の色,あるいは輝度値(グレースケール画像).
 thickness – 矩形の枠線の太さ. CV_FILLED などの負の値の場合,塗りつぶされた矩形が描かれます.
 lineType – 枠線の種類. line() を参照してください.
 shift – 点の座標において,小数点以下の桁を表すビット数.
となります。
※参考:描画関数 — opencv 2.2 documentation


次にputTextですが、文字列を画像上に描画することが出来、
こちらのように定義されております。
putText(Mat& img, const string& text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=8, bool bottomLeftOrigin=false)
引数ですが、
 img – 画像.
 text – 描かれる文字列.
 org – 文字列の左下角の,画像中の座標.
 fontFace – フォントの種類.以下のうちの1つ. FONT_HERSHEY_SIMPLEX , FONT_HERSHEY_PLAIN , FONT_HERSHEY_DUPLEX , FONT_HERSHEY_COMPLEX , FONT_HERSHEY_TRIPLEX ,    FONT_HERSHEY_COMPLEX_SMALL , FONT_HERSHEY_SCRIPT_SIMPLEX , FONT_HERSHEY_SCRIPT_COMPLEX .また,各フォントIDを, FONT_HERSHEY_ITALIC と組み合わせて,斜体文字にすることもできます.
 fontScale – フォントのスケールファクタ.これがフォント特有の基本サイズに掛け合わされます.
 color – 文字列の色.
 thickness – フォントの描画に利用される線の太さ.
 lineType – 線の種類.詳細は line を参照してください.
 bottomLeftOrigin – true の場合は画像データの原点が左下,そうでない場合は左上になります.
となります。
※参考:描画関数 — opencv 2.2 documentation

■rectangleとputTextを用いて動画を作成してみる

先ほど記載しました通り、rectangleやputTextでは座標を指定して描画しますので、
動いているように見せるためには座標をフレーム毎に変更させればOKです。

今回作成したコードはこちら。

import cv2
import numpy as np

BASEPOS_WIDTH = 100
BASEPOS_HEIGHT = 100
HOLISONTALNUM = 4
VERTICALNUM = 4
LOOP_COUNT = 12

VIDEO_SIZE = (HOLISONTALNUM * BASEPOS_WIDTH,VERTICALNUM * BASEPOS_HEIGHT)
FONT = cv2.FONT_HERSHEY_SIMPLEX

outputFile = 'ImgVideo.mp4'
fourcc = cv2.VideoWriter_fourcc('m','p','4', 'v')
frameRate = 10.0

def MakeSampleVideo():
    video  = cv2.VideoWriter(outputFile, fourcc, frameRate, (VIDEO_SIZE))

    imgNum = 0
    count = 0

    while True: 
        white_img = np.zeros((VERTICALNUM * BASEPOS_HEIGHT, HOLISONTALNUM * BASEPOS_WIDTH,3), dtype='uint8')
        white_img.fill(255)
        for v in range(VERTICALNUM):
            for h in range(HOLISONTALNUM):    
                left =  BASEPOS_WIDTH * h
                right = BASEPOS_WIDTH * (h+1)
                top = BASEPOS_HEIGHT * v
                bottom = BASEPOS_HEIGHT * (v+1)

                white_img = cv2.rectangle(white_img,(left,top),(right,bottom),(0,0,0),2)
                cv2.putText(white_img,str(imgNum),(left+25,bottom-25),FONT,2,(0,0,0),3)

                #動画を見るだけならこちらを利用
                # cv2.imshow('sample', white_img)
                # if cv2.waitKey(100) & 0xff == ord('q'):
                #     break

                #動画を保存するとき
                video.write(white_img)

                if imgNum > (VERTICALNUM * HOLISONTALNUM - 2):
                    imgNum = 0

                else:
                    imgNum+=1

        if count > LOOP_COUNT:
            break
        else:
            count += 1

    video.release()

MakeSampleVideo()

まずベースとなる画像ですが、

white_img = np.zeros((VERTICALNUM * BASEPOS_HEIGHT, HOLISONTALNUM * BASEPOS_WIDTH,3), dtype='uint8')
white_img.fill(255)

で作成した真っ白な画像になります。
画像サイズは、400px × 400pxです。

今回rectangle、putTextの描画位置を動かしている処理はこちら。

left =  BASEPOS_WIDTH * h
right = BASEPOS_WIDTH * (h+1)
top = BASEPOS_HEIGHT * v
bottom = BASEPOS_HEIGHT * (v+1)

white_img = cv2.rectangle(white_img,(left,top),(right,bottom),(0,0,0),2)
cv2.putText(white_img,str(imgNum),(left+25,bottom-25),FONT,2,(0,0,0),3)

BASEPOS_WIDTH、BASEPOS_HEIGHTはそれぞれ100pxで、
v、hは0~3が入ります。
上記パラメータを用いて、
left、right、top、bottomという、座標位置を算出。
これにより、
rectangleでは100px × 100pxの四角形を左上から右下へ順番に表示させることが出来ます。
putTextも同様に、左下の座標をleft,bottomで指定しております。

後は、一周だけだと寂しいので今回は10週数字が動くようにしました。

if count > LOOP_COUNT:
    break
else:
    count += 1

ここはもう少し工夫すれば簡潔に書くことが出来るかも。

■最後に

今回はrectangleやputTextを用いて、OpenCVが用意するライブラリで描画した四角形や文字で動かし、
動画を作成してみました。

作ってみたけど、30fpsや60fpsにすると流石に早すぎて目が追えない。。
まぁ、低速であれば使い道はあるかな??
結局無駄なものを作ってしまったかも。。


nodejsでサーバーのCPUやメモリリソースを取得してみる

前回、Pythonでサーバー上のリソースを取得するためのアプリを作成してみました。
elsammit-beginnerblg.hatenablog.com

前回のアプリはfrontをjavascriptで作成したのですが、どうせならReactを使用してしっかり作ってみようかな?
と思い、再挑戦することに。

どうせならサーバーサイドもpythonではなく、nodejsでも使ってみようと思ったのですが、、、
nodejsでCPUやメモリリソースのチェックの仕方を知らない!!

ということで、今回はnodejsでリソース取得する方法を調べましたので備忘録として残そうと思います。



■CPU・メモリリソース取得するためのモジュール

nodejsでリソースを取得するには、
osモジュールを使用すれば取得できます。

下記でモジュールを読み込めばOK。

const os = require('os');

■CPUリソース取得

では、osモジュールを用いてCPUリソースを取得するためには、
こちらのコードを実行すればOKです。

const cpuInfos = os.cpus();

こちらを実行すると、
各コア毎のCPUリソースがオブジェクト配列で取得できます。

[
  {
    model:*****
    speed: ****,
    times: {
      user: ****,
      nice: 0,
      sys: ****,
      idle: ****
      irq: 0
    }
  },
  {
    model: ****
    speed: ****,
    times: {
      user: ****,
      nice: ****,
      sys: ****,
      idle: ****,
      irq: ****
    }
  },
  ・・・
]

timesオブジェクトの中身はそれぞれ下記の意味になります。

・user CPUがユーザーモードで費やしたミリ秒数。
・nice CPUがniceモードで費やしたミリ秒数。
・sys CPUがsysモードで費やしたミリ秒数。
・idle CPUがアイドルモードで費やしたミリ秒数。
irq CPUがirqモードで費やしたミリ秒数。

■メモリリソース取得

次はメモリリソースの取得です。
メモリはそれぞれ下記の通り、トータルメモリや空きメモリの取得が行えます。

const totalMem = os.totalmem();
const freeMem = os.freemem();

■実際にCPUやメモリリソースを取得するためのサーバーアプリを作成

では、先ほどのCPUやメモリを取得するWebアプリケーションを簡単に作成してみました。
コードはこちら。

const os = require('os');
const express = require('express');

const app = express();

getCpuInfo = () =>{
    const nowDate = new Date();
    const cpuInfos = os.cpus();
    let cpuUsage = 0;
    let cpuUsageRet = [];

    cpuInfos.forEach((element) => {
        let total = 0;
        for(let type in element.times){
            total += element.times[type];
        }
        cpuUsage += (1 - (element.times["idle"] / total))* 100;
    });
    cpuUsage = (cpuUsage / count).toFixed(2);
    cpuUsageRet = {cpuUsage:cpuUsage,date:nowDate};
    return cpuUsageRet;
}

getMemInfo = () =>{
    const nowDate = new Date();
    const nowDateStr = `${nowDate.getFullYear()}/${nowDate.getMonth()+1}/${nowDate.getDate()} ${nowDate.getHours()}:${nowDate.getMinutes()}:${nowDate.getSeconds()}`
    const totalMem = os.totalmem();
    const freeMem = os.freemem();

    const memUsage = ((1 - freeMem / totalMem)*100).toFixed(2);
    return {
        date:nowDateStr, memUsage:memUsage, 
    };
}

app.listen(8080, ()=>{
    console.log("Server Start");
});

app.get('/', (req, res) =>{
    const cpuUsage = getCpuInfo();
    const memUsage = getMemInfo();
    const usage = {CPU:cpuUsage, Memory:memUsage};
    res.json(usage);
    res.end();
});

expressを使用しておりますが、コードは単純で、
getリクエストを受け付けたら、CPUやメモリリソースを取得し、jsonでfrontに返すコードになります。

こちらを、

node ファイル名.js

等で実行頂いた後にWebブラウザ等でアクセスすると、
CPUやメモリリソースが格納されたjsonデータが取得できるかと思います。

■最後に

今回はnodejsでリソースを取得する方法についてまとめました。
react + nodejsでリソース取得用のアプリを作成中です。
作成出来ましたら、ブログで報告していきたいと思います。



cifsをマウントしたディレクトリでもリンク作成できるようにする

先日、リンクが作成出来ないことに起因してnpmやnpxでエラーが発生してしまいました。
一応、npm、npxコマンド実行時にオプションを付与することで回避できたのですが、、、
elsammit-beginnerblg.hatenablog.com


そもそも、
なんでリンク作成出来ないのだろうか?リンク作成出来るようにするための方法は?
が気になったので調査は継続していました。

原因と解決策が分かりましたので備忘録としてまとめておきます。



■原因

原因というほどのものではないのですが、、、
cifsファイルシステムのマウントを行う際に、
シンボリックリンクの作成を有効化するために明示的にオプションを付与する必要がある
とのことです。

自分の場合はnasでしたがsambaも同様で、
cifsファイルシステムの場合は全て該当します。

要するに、、

mount -t cifs ~~

でマウントした場合にはオプションの付与が必要になる。ということです。

では、なぜオプションを付与しないとシンボリックリンクが有効にならないのか?
こちらは調べたのですが具体的な理由が分からず。。。
cifsはwindowsのファイル共有サービスだからwindows以外のOSの場合には
デフォルトである程度制限かけているのでしょうか?

■対策

先ほど記載した通り、オプションを付与すればシンボリックリンクの作成が行えます。
このオプションですが、mfsymlinksオプションになります。

このmfsymlinksオプションをmountコマンド実行時に付与すればOKです。
例えば、、

mount -t cifs //ipaddr/pathes -o username=username,password=password,file_mode=0755,dir_mode=0755,mfsymlinks

といった形でコマンドを実行すればOKです。

これでマウントしたディレクトリにリンクの作成が行えるようになっているかと思います。

■(補足)cifsとは?

Common Internet File Systemの略。
WIndowsのファイル共有サービスで利用されるプロトコルであるSMBを拡張し、
Windows以外のシステムでも利用できるようにしたもの。
※SMB:Server Message Blockの略
参考資料:https://e-words.jp/w/CIFS.html

■最後に

今回はcifsファイルシステムに対してリンク作成を有効にするための方法をまとめました。
なぜオプションを付与して明示的にリンク作成を有効にしなければならないのか、理由までは正確に分かっていないのがはがゆい。。
もう少し頑張って調べます。



ENOTSUP: operation not supported on socket, symlinkを解決した話

最近、以前勉強していたReactやnodejsを再勉強中。
そこである日、

npm install

npx create-react-app

を実行したところ、

ERR! code ENOTSUP
npm ERR! syscall symlink
npm ERR! path ../mime/cli.js
npm ERR! errno -95
npm ERR! ENOTSUP: operation not supported on socket, symlink '

といったエラーが発生してしまいました。

今回は本エラー発生の原因とその解決方法を調べたので備忘録として残しておきたいと思います。


■現象発生原因

このエラーは、nasサーバーを

mount -t cifs

でマウントしたマウント先のディレクトリでのみ発生しており、
ディレクトリでは問題なくnpm installやnpx create-react-appは実行出来ている。

このため、コマンドが実行出来ていないわけではなさそう。。

そこで調べてみると気になるやり取りを発見。
github.com

読んでみるとリンクが作成出来ないことが起因しているエラーっぽい?
ということで試しに、test.txtファイルを作成した上で

ln -s test.txt test2.txt

を実行。

すると、

ln: シンボリックリンク 'test2.txt' の作成に失敗しました: サポートされていない操作です

とエラーが発生しシンボリックリンクが作成出来ない。
確かにリンク作成が出来ないことが起因してエラーが発生しているもよう。

■解決方法

エラーの原因がリンク作成できなかったことが起因しているので、
リンクを作成しないようにコマンドを実行すれば良さそう。

ということで、先ほどのwebページ(github issue)を確認してみると、

    • no-bin-linksオプションを付けるとリンク作成せずにコマンドの実行が出来るそう。

ということで、それぞれ

npm install --no-bin-links

npx create-react-app --no-bin-links

とオプションを付与して実行してみたところ、
エラーが発生せずインストール出来ることを確認。

何とか使用できるようになって満足。

■最後に

今回はnpmやnpxを実行した時にsymlinkのエラーに対する対応方法をまとめました。
なぜリンクが作成できなかったのか?、リンクを作成するためにはどうすればよいか?に関しては継続して調査するとして、、、
まずは、

npm install

npx create-react-app

が使えるようになったので良かった。



OpenCV.jsで動画再生を実施

先日、OpenCV.jsの導入手順や画像をグレーやカラーに変化して表示させてみました。
elsammit-beginnerblg.hatenablog.com

今回はOpenCV.jsで動画の再生を試してみたいと思います。



OpenCV.js公式サイトのサンプルを試してみる

OpenCV.jsの公式サイトには動画再生のサンプルコードが掲載されております。
OpenCV: Getting Started with Videos

しかしながら、、
こちらのコードを実行してみたところ、上手く動かない。。
動いてもなぜかすぐにエラーで止まってしまう。。
エラーコードは、

Uncaught 6640648

とよく分からない。。

■なぜエラーとなってしまったか?

サンプルコードがなぜ動かなかったのか調査してみました。
結果、どうやらサンプルコードだと
src、dstが開放されずたまり続けてしまい最終的にエラーで落ちてしまっているようです。

このため、

cv.imshow('canvasOutput', dst);

setTimeout(processVideo, delay);

の後に、

src.delete();
dst.delete();

で開放させてあげる必要があるようです。

OpenCV.jsでの動画再生コード紹介

では作成した動画再生のコードをご紹介します。
まずはhtmlです。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>OpenCV.js sample</title>
    </head>
    <body>
        <h2>OpenCV.js sample code</h2>
        <p id="status">OpenCV.js is loading...</p>
        <div>
            <select name="imgType" id="ImgType">
                <option value="カラー">カラー</option>
                <option value="グレー">グレー</option>
                <option value="2値化">2値化</option>
            </select>
            <div class="inputoutput">
                <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
            </div>
            <div class="inputoutput">
                <div class="caption">canvasOutput</div>
                <canvas id="canvasOutput" ></canvas>
            </div>
        </div>
    </body>
    <script src="js/ImgScript.js" type="text/javascript"></script>
    <script async src="js/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
</html>

サンプルではvideoタグで再生した動画データをOpenCVでグレーアウト処理を行いとなりに表示するような処理でした。
しかし、、、
同じ動画を表示させる必要はないので、
javascriptのDOMを用いて新規作成したvideoタグを表示せずにOpenCVによる画像処理でcanvasタグに表示するようにいたしました。

さらに、javascriptのコードはこちら。

let inputElement = document.getElementById('fileInput');
let selectElement = document.getElementById('ImgType');

let filename = "";

function onOpenCvReady() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}

inputElement.addEventListener('change', (e) => {
    filename = e.target.files[0];
    InitVideo();
    SettingVideo();
}, false);

let videoElement = document.createElement('video');

InitVideo = () =>{
    videoElement.src = URL.createObjectURL(filename);
    videoElement.height = 540;
    videoElement.width = 960;
    videoElement.autoplay = true;
    videoElement.muted = false;
    videoElement.controls = false;
    videoElement.loop = true;
}

SettingVideo = () =>{
    let src = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
    let dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC1);
    let cap = new cv.VideoCapture(videoElement);
    try{
        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }
        cv.imshow('canvasOutput',dst);
        let delay = 1000/30;
        setTimeout(SettingVideo, delay);
        src.delete();
        dst.delete();
    }catch(err){
        console.error(err);
        src.delete();
        dst.delete();
        InitVideo();
    }
}

先ほど記載した通り、videoタグをcreateElementで作成し初期化処理でvideoタグのサイズやattributeを設定しました。

let videoElement = document.createElement('video');

InitVideo = () =>{
    videoElement.src = URL.createObjectURL(filename);
    videoElement.height = 540;
    videoElement.width = 960;
    videoElement.autoplay = true;
    videoElement.muted = false;
    videoElement.controls = false;
    videoElement.loop = true;
}

OpenCV.jsによる画像処理とcanvasタグへの表示はこちら。

SettingVideo = () =>{
    let src = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
    let dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC1);
    let cap = new cv.VideoCapture(videoElement);
    try{
        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }
        cv.imshow('canvasOutput',dst);
        let delay = 1000/30;
        setTimeout(SettingVideo, delay);
        src.delete();
        dst.delete();
    }catch(err){
        console.error(err);
        src.delete();
        dst.delete();
        InitVideo();
    }
}

先ほど記載しました通り、

cv.imshow('canvasOutput',dst);
let delay = 1000/30;
setTimeout(SettingVideo, delay);
src.delete();
dst.delete();

により、srcやdstを都度開放しております。

ただグレーアウトした動画を流すだけだとつまらないところがあったので、
readで読み取ったデータに対して、
selectタグで切り替えた動画種類に応じて画像処理を施すことにより、
流す動画を切り替えるようにしました。

        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }

■実際に動かしてみる

先ほどご紹介したコードを実行するとこちらのようになります。
選択した動画種類に応じて表示する動画を切り替えて再生させることが出来ました。
こちらは動画を再生してもエラーが出ることはありません。
※こちらの動画はcanvasのサイズを480x270に変更しております。

■最後に

今回はOpenCV.jsで動画再生を試してみました。
やはり画像処理を行っている関係で初回動画読み込みに少し時間を要しておりました。
一回読み込めてしまえばカクつくことなく表示していたので、再生するだけならそこまで重くはないのかな?
初回読み込みだけ気になるだけであとは普通に使えそうです。


javascript用OpenCVであるOpenCV.js試してみた

先日OpenCVに関して調べものをしていたところ、
javascriptで動作させることが出来るOpenCV.jsなるものを発見!!

早速使ってみましたので今回はセットアップも含めて備忘録残しておこうと思います。



OpenCV.jsとは?

OpenCVの公式ページにはこちらの通り説明がありました。
OpenCV.js is a JavaScript binding for selected subset of OpenCV functions for the web platform. It allows emerging web applications with multimedia processing to benefit from the wide variety of vision functions available in OpenCV. OpenCV.js leverages Emscripten to compile OpenCV functions into asm.js or WebAssembly targets, and provides a JavaScript APIs for web application to access them. The future versions of the library will take advantage of acceleration APIs that are available on the Web such as SIMD and multi-threaded execution.

翻訳すると、
OpenCV.jsは、Webプラットフォーム用のOpenCV関数の選択されたサブセット用のJavaScriptバインディングです。これにより、マルチメディア処理を備えた新しいWebアプリケーションは、OpenCVで利用可能なさまざまなビジョン機能の恩恵を受けることができます。OpenCV.jsは、Emscriptenを利用してOpenCV関数をasm.jsまたはWebAssemblyターゲットにコンパイルし、WebアプリケーションがそれらにアクセスするためのJavaScriptAPIを提供します。ライブラリの将来のバージョンでは、SIMDやマルチスレッド実行などのWebで利用可能なアクセラレーションAPIを利用する予定です。

となります。
※参考:OpenCV: Introduction to OpenCV.js and Tutorials

要するに、OpenCV関数をJavascriptでも使用できるようにJavascriptでラッピングしたライブラリということですね。

OpenCV.js利用環境構築

pythonなどでOpenCVを利用する場合には環境構築がとても面倒なのですが、、、
OpenCV.jsはとても簡単です。

OpenCV.jsをダウンロードし、
javascript利用時に他のjavacriptライブラリと同様にOpenCV.jsを読み込めばOK。

公式では、
githubで公開されている、
opencv-{VERSION_NUMBER}-docs.zip
をダウンロードする
github.com

https://docs.opencv.org/3.4.0/opencv.jsといったように、
OpenCV.jsファイル単体をダウンロードする

2つの方法が紹介されておりました。

ただ、、、
①はzipファイルで86MBと大きく、解凍するにも時間を要するので
②の方法が良いかと思います。
※私も②でOpenCV.jsをダウンロードしました。

後は、フロントエンドで用いる場合には、

<script src="opencv.js" type="text/javascript"></script>

として、読み込めばOKです。

■画像データのカラー・グレースケールの切り替えを実現してみる
では、OpenCV.jsで画像データのカラー・グレースケールの切り替えを試してみます。

まずはhtmlファイルを作成していきます。
コードはこちら。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>OpenCV.js sample</title>
    </head>
    <body>
        <h2>OpenCV.js sample code</h2>
        <p id="status">OpenCV.js is loading...</p>
        <div>
            <select name="imgType" id="ImgType">
                <option value="カラー">カラー</option>
                <option value="グレー">グレー</option>
            </select>
            <div class="inputoutput">
                <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
            </div>
            <div class="inputoutput">
                <div class="caption">canvasOutput</div>
                <canvas id="canvasOutput" ></canvas>
            </div>
        </div>
    </body>
    <script src="js/ImgScript.js" type="text/javascript"></script>
    <script async src="js/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
</html>
<div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>

にて指定したファイルを、

<canvas id="canvasOutput" ></canvas>

上に画像を表示し、

<select name="imgType" id="ImgType">
    <option value="カラー">カラー</option>
    <option value="グレー">グレー</option>
</select>

にて、カラー・グレースケールの切り替えを行るように
OpenCV.jsライブラリを用いたjavascriptを作成していきます。

javascriptのコードはこちら。

let inputElement = document.getElementById('fileInput');
let selectElement = document.getElementById('ImgType');

let filename = "";

inputElement.addEventListener('change', (e) => {
    filename = e.target.files[0];
    SettingImage();
}, false);

selectElement.addEventListener('change', () => {
    if(filename === ""){

    }else{
        SettingImage();
    }
}, false);

SettingImage = () =>{
    let imgElement = document.createElement('img');
    imgElement.src = URL.createObjectURL(filename);
    imgElement.onload = () => {
        let dsize = new cv.Size(imgElement.naturalWidth/2, imgElement.naturalHeight/2);
        let mat = cv.imread(imgElement);  
        let dst = new cv.Mat();
        cv.resize(mat, dst, dsize, 0, 0, cv.INTER_AREA);
        if(selectElement.value === "グレー"){
            cv.cvtColor(dst,dst,cv.COLOR_RGB2GRAY);
        }
        cv.imshow('canvasOutput', dst);
        mat.delete();
        dst.delete();
    }
}

function onOpenCvReady() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}

まず、OpenCV.jsを利用できるまでの初期化処理に時間を要するため、

function onOpenCvReady() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}

などにより準備完了をチェックした方が良いです。

OpenCV.jsを用いて画像の表示やカラー・グレースケールへの変換実施している箇所はこちらになります。

SettingImage = () =>{
    let imgElement = document.createElement('img');
    imgElement.src = URL.createObjectURL(filename);
    imgElement.onload = () => {
        let dsize = new cv.Size(imgElement.naturalWidth/2, imgElement.naturalHeight/2);
        let mat = cv.imread(imgElement);  
        let dst = new cv.Mat();
        cv.resize(mat, dst, dsize, 0, 0, cv.INTER_AREA);
        if(selectElement.value === "グレー"){
            cv.cvtColor(dst,dst,cv.COLOR_RGB2GRAY);
        }
        cv.imshow('canvasOutput', dst);
        mat.delete();
        dst.delete();
    }
}

OpenCV.jsにてcv.imreadで画像を読み取る場合、
imgタグを指定する必要があるようです。
このため、あえて

 let imgElement = document.createElement('img');
imgElement.src = URL.createObjectURL(filename);

によりimgタグを作成の上、imgタグのsrcに読み込む画像パスを指定します。

後は、pythonC++C#と似た感じでimreadでmat型変数に代入の上、
グレースケールに変換する場合には、
cv.cvtColor(dst,dst,cv.COLOR_RGB2GRAY);
で変換すればOK。

最後にimshowでcanvasタグに表示させればOK。

cv.imshow('canvasOutput', dst);

imshowの第1引数にcanvasのidを指定します。

■コード動作させてみる
先ほどのコードを動作させるとこちらのように動作します。
※ファイル選択後の動作になります。

■最後に

今回はjavascriptで動作させるOpenCVである、OpenCV.jsについてご紹介しました。
ブラウザで動作させるのが面白いのでもう少し使ってみたいと思います。

使った感想や使い方は備忘録として残していきたいと思います。