電算倶楽部 富山県のコンピュータ社会人サークル

富山県、特に滑川市、富山市、魚津市周辺で活動している社会人サークルです。

ノーコード開発を触ってみた 1

はじめに

電算倶楽部の定例会で、ノーコード開発を試してみたことについて話して、実際にその場で作ってみました。 また、電算倶楽部の収支登録・編集・閲覧アプリを作ってみました。 数日間触っただけなのであまり語れることはありませんでしたが、初心者なりに実施内容や感想をここにメモしておこうかと思います。

ノーコード開発について

wikipedia - ノーコード開発プラットフォーム https://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E9%96%8B%E7%99%BA%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0

ノーコード開発プラットフォーム (英: No-code development platform, NCDP) (NoCode(ノーコード)とも言う) は、プログラマーやノンプログラマーが、従来のコンピュータ・プログラミングの代わりに、グラフィカル・ユーザー・インタフェースや設定を通してアプリケーション・ソフトウェアを作成することを可能にする。ノーコード開発プラットフォームは、アプリケーション開発プロセスを迅速化するために設計されているため、ローコード開発プラットフォームと密接に関連している。これらのプラットフォームは、企業において、モバイル化が進行する労働力と、有能なソフトウェア開発者の供給が限られているという2つのトレンドに対処するために、人気が高まっている[1]。

プラットフォームはソフトウェアの開発基盤として、その機能、統合、市場ニッチが大きく異なる。データ収集やワークフローなどの特定のビジネス機能のみに特化したアプリケーションもあれば、エンタープライズ・リソース・プランニング (ERP)ツール全体をモバイル型機器 (mobile form factor) に統合しようとするアプリケーションもある[2]。

ノーコード開発プラットフォームは、コンピュータサイエンスではビジュアルプログラミング言語として知られている[3]。

試してみたもの

事前にウェブで情報収集し、ジャンル別によく名前を見かけるものをピックアップしました。

  1. Webflow (ウェブ)
  2. Glide (モバイル)
  3. Bubble (業務)
  4. Appsheet (大手)

この中で、比較的簡単に見栄えの良いものが作れそうな「Glide」を取り上げ、電算倶楽部の収支情報を登録・閲覧できるアプリを作ってみました。

Glideでどんなアプリが作れるのか?

PWAという、ウェブで動くスマホに特化したアプリが作れます。 さらに、ブラウザからインストールすることで普通のアプリのように使えるようになるという特徴があります。

この例ではAndroidChromeを使っています。 Glideで作ったアプリのページを開き、メニューからインストールを選びます。

f:id:s-densan:20210109203155j:plain

f:id:s-densan:20210109203454j:plain

すると、ホーム画面にそのアプリのアイコンが作成されます。

f:id:s-densan:20210109203742j:plain

作ってみたアプリ紹介

できること

  • イベントとその収入(集金)・支出(場所代)の情報、領収書へのリンクの登録・編集・閲覧
  • 物品購入費やサーバ代などのサービス使用料金の登録・編集・閲覧
  • 年度毎の集計情報の閲覧

使用イメージ

こんな感じです。 よくあるスマホアプリのような動作をします。

f:id:s-densan:20210110194632g:plain

アプリ開発の説明は次の記事で書きます。

CADと3Dプリンタで牛型のスタンプを作る

あけましておめでとうございます。 今年はCADの勉強をしたいと考えていますので、早速CADでかんたんなスタンプを作成し、3Dプリンタで印刷してみました。 もし出来が良ければ年賀状に使ってみようかな…

イラストイメージ様の無料イラストを使わせていただきました。(ありがとうございます!) illustimage.com

使ったツールは以下のとおりです。

Inkscapeで画像をラスタ形式からベクタ形式に変換

FreeCADで読み込むために、画像をベクタ形式であるSVGファイルに変換する必要があります。 Inkscapeを使い、画像の輪郭を自動でトレースさせます。

Inkscapeに画像を読み込み、メニューから「パス」「ビットマップのトレース」を選択します。

f:id:s-densan:20210104194538p:plain

以下のように自動で輪郭がトレースされました。

f:id:s-densan:20210104194649p:plain

不要なものは削除し、SVGで保存します。

FreeCADでスタンプ形状にする

FreeCADを開き、DraftとしてSVGを開きます。

f:id:s-densan:20210104195901p:plain

輪郭しかないのでペラペラだし、どこが穴でどこが埋まっているかわからない状態です。 1cm押し出して、集合演算で整えていきます。 また、スタンプの持ち手として、幅3cm奥行き3cm高さ1cmの台座を付けます。

f:id:s-densan:20210104200200p:plain

口は細かくなりそうなので穴を空けず。角はもともと2つありましたが、いじっているうちに1ついつの間にか消えていました。

出来上がったら、Curaで読み込めるようSTL形式で保存します。

CuraでGCode作成

Curaで積層式の3Dプリンタなので、不安定な形状にならないよう回転させたりして調整します。 Curaの予測で、プリント時間は約1時間とのこと。

GCodeファイルを3DプリンタのSDカードに書き出します。

いざ、3Dプリンタで印刷!

ANYCUBIC MEGA-S という3Dプリンタを使用しました。 2年前に買ってから押入れで眠っていましたが ちゃんと動作してよかったです。

素材はPLAです。硬い素材なのですが、手持ちはこれしかなかったので。

作り始め

f:id:s-densan:20210104201357j:plain

20分後

f:id:s-densan:20210104201425j:plain

40分後

f:id:s-densan:20210104201444j:plain

完成

f:id:s-densan:20210104201640j:plain

使ってみた

f:id:s-densan:20210104231310p:plain

うーーーーん。一応ヤスリで削ってなめらかにしてみて、この程度です。 素材をTPUにすると良いかもですね。 年賀状に使うのは諦めました。

終わりに

3Dプリンタの復活と、3D CADの勉強の第一歩として作ってみましたが、単純な形状だったので大きなトラブルなく印刷できました。 次はもっと実用的なものを目指します。


2021-01-09追記

印刷風景

f:id:s-densan:20210109201120g:plain

クリップボード取り出しコマンド

クリップボードからデータを取り出すプログラムを作ったので紹介します。 ファイルやExcelなどの(多分)Windows固有のデータも取り出せます。

経緯

クリップボードのデータを様々なデータ形式で取り出せる手段はあまりないように感じます。せいぜいテキスト情報、できてHTMLや画像情報のみ。 そこでクリップボード情報を手軽に取り出すコマンドラインプログラムを作成しました。 単体でも使えるし、ほかプログラムと連携させることもできます。 動作環境はWindows限定です。かつ、.Net 5ランタイムが必要です。

インストール

Githubで公開しています。 zipを解凍して任意のフォルダに格納してください。 必要に応じて環境変数に追加してください。

github.com

コマンドの説明

cclip.exe [オプション]

オプション

  • -l, --list
    クリップボードのフォーマット一覧を表示します。
  • -f, --format フォーマット名
    出力するフォーマットを指定します。
  • -j, --json
    JSON形式で出力します。
  • -a --all
    JSON出力時、すべてのフォーマットについて出力します。
  • -o --output ファイルパス
    標準出力ではなくファイルに出力します。

HTMLテキスト"電算倶楽部"をコピーした状態で実行

テキスト情報表示

コマンド

cclip.exe
電算倶楽部

フォーマット一覧表示

コマンド

cclip.exe -l

出力

HTML Format
Text
UnicodeText
System.String
Locale
OEMText

HTMLフォーマットで表示

コマンド

cclip.exe -f "html format"

出力

Version:0.9
StartHTML:0000000360
EndHTML:0000001068
StartFragment:0000000396
EndFragment:0000001032
SourceURL:https://blog.hatena.ne.jp/-/globalheader/f7f8f9/242527/admin?device=pc&show_upgrade_pro=yes&brand=hatenablog#https%3A%2F%2Fblog.hatena.ne.jp%2Fs-densan%2Fs-densan.hatenablog.com%2Fedit%3Fsyntax%3Dmarkdown%26title%3D%26entry%3D26006613667805093
<html>
<body>
<!--StartFragment--><a class="current-blog-title" href="https://s-densan.hatenablog.com/" style="color: rgb(247, 248, 249); display: block; line-height: 37px; text-decoration: none; max-width: 180px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;">電算倶楽部</a><!--EndFragment-->
</body>
</html>

ファイル出力

コマンド

cclip.exe -f "html format" -o out.html

out.htmlが出力される。

画像をコピーした状態で実行

コマンド

cclip.exe -l

結果

Preferred DropEffect
Bitmap
System.Drawing.Bitmap
System.Windows.Media.Imaging.BitmapSource
ContentSourceUserActivity
PNG

画像データを出力

コマンド

cclip.exe

結果

iVBORw0KGgoAAAANSUhEUgAAAHgAAAAnCAYAAADEvIzwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAkxSURBVHhe7ZsJUBVXFoZ/UGRR0cHAiMsEURTUCWoUUbOUu2iVFUNMppwg4Erc18SgWCCKmOCGS5AgwiiKwRAUNRklaCUxWsGFxCUDDo4boomgBQqy6dxzuR2aR/d7j01Tr/qr6uL16X79us+595z/3C7MSsvKn0HDZDEXfzVMFC3AJo4WYBNHC7CJowXYxNECbOJoATZxtACbOFqATRwtwCbOCwtwWvq3mD7rA9y8dUtYavLs2TPsT0rCgiVL8Pv934XVMI8fP8aHgYGI3rkTT58+FdY/Jw8ePoTPFH9s3rZVWBqf5uKvKmfPn8e0gJlir244tnfE9shI
(略)

Base64で符号化して出力。画像形式はPNG

JSON形式で出力

コマンド

cclip.exe -j

結果

[
  {
    "format": "Bitmap",
    "data": "iVBORw0KGgoAAAANSUhEUgAAAHgAAAAnCAYAAADEvIzwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAkxSURBVHhe7ZsJUBVXFoZ/UGRR0cHAiMsEURTUCWoUUbOUu2iVFUNMppwg4Erc18SgWCCKmOCGS5AgwiiKwRAUNRklaCUxWsGFxCUDDo4boomgBQqy6dxzuR2aR/d7j01Tr/qr6uL16X79us\u002B595z/3C7MSsvKn0HDZDEXfzVMFC3AJo4WYBNHC7CJowXYxNECbOJoATZxtACbOFqATRwtwCbOCwtwWvq3mD7rA9y8dUtYavLs2TPsT0rCgiVL8Pv934XVMI8fP8aHgYGI3rkTT58\u002BFdY/Jw8ePoTPFH9s3rZVWBqf5uKvKmfPn8e0gJlir(略)"
  }
]

ファイルに保存

コマンド

cclip.exe -f bitmap -o out.png

out.pngに保存される。

ファイルをコピーした状態で実行

一覧を表示

コマンド

cclip.exe

結果

C:\Users\(ユーザ名)\Desktop\cclip_v0.1.0\cclip.dll
C:\Users\(ユーザ名)\Desktop\cclip_v0.1.0\cclip.exe
C:\Users\(ユーザ名)\Desktop\cclip_v0.1.0\cclip.runtimeconfig.json
C:\Users\(ユーザ名)\Desktop\cclip_v0.1.0\cclip_lib.dll

フォーマット一覧

コマンド

cclip.exe -l

結果

Shell IDList Array
DataObjectAttributes
DataObjectAttributesRequiringElevation
Shell Object Offsets
Preferred DropEffect
AsyncFlag
FileDrop
FileNameW
FileName
FileContents
FileGroupDescriptorW

JSON形式で出力

コマンド

cclip.exe -j

結果

[
  {
    "format": "FileDrop",
    "data": [
      "C:\\Users\\(ユーザ名)\\Desktop\\cclip_v0.1.0\\cclip.dll",
      "C:\\Users\\(ユーザ名)\\Desktop\\cclip_v0.1.0\\cclip.exe",
      "C:\\Users\\(ユーザ名)\\Desktop\\cclip_v0.1.0\\cclip.runtimeconfig.json",
      "C:\\Users\\(ユーザ名)\\Desktop\\cclip_v0.1.0\\cclip_lib.dll"
    ]
  }
]

線分と円の当たり判定

概要

前の記事の続きで、カメラで撮った画像から線を検出し、それが正しい位置にあるかを判定するプログラムを目指して開発していきます。 今回は線分と円が交わっているかを判定するプログラムについて考えてみます。

まず解法(アルゴリズム)を考えていきます。当たり判定を行うライブラリもあるかと思いますが、このくらいならば高校数学の知識でアルゴリズムを作ることができます。

問題の整理

線分と円が交わっているか、と書いても曖昧なので、以下のように整理します。

問題: 点$O$を中心とする半径$r$の円(内部含む)と、点$A$と点$B$を結ぶ線分が衝突している条件を求めよ。ただし、円の半径$r$は$0$よりも大きく、点$A$と点$B$は同一でないものとする。

色々と解き方がありますが、今回はベクトルで解く1前提で、もう少し条件を整理します。

次の1~4を満たす点$P$が存在すること。

  1. 点$P$は点$O$を中心とする半径$r$の円に含まれる。 すなわち、 $ |\overrightarrow{OP}| \le r $が成り立つ。
  2. 点Pは線分$AB$上に位置する。 すなわち、ある0以上1以下の実数$s$が存在し、 $\overrightarrow{OP} = (1-s)\overrightarrow{OA} + s\overrightarrow{OB}$ が成り立つ。
  3. $r > 0$
  4. $\overrightarrow{OA} \not= \overrightarrow{OB}$

f:id:s-densan:20200922210148p:plain

解法

詳細は飛ばしますが、次の2次不等式を解けばOKです。

$$ (|\overrightarrow{OA}|^{2} + |\overrightarrow{OB}|^{2} - 2\overrightarrow{OA} \cdot \overrightarrow{OB})s^{2} + (-2|\overrightarrow{OA}|^{2} + 2\overrightarrow{OA} \cdot \overrightarrow{OB})s + (|\overrightarrow{OA}|^{2} - r^{2}) \le 0 $$

簡単のために、実数$a$,$b$,$c$を定義しておきます。 $$ a = |\overrightarrow{OA}|^{2} + |\overrightarrow{OB}|^{2} - 2\overrightarrow{OA} \cdot \overrightarrow{OB} $$ $$ b = -2|\overrightarrow{OA}|^{2} + 2\overrightarrow{OA} \cdot \overrightarrow{OB} $$ $$ c = |\overrightarrow{OA}|^{2} - r^{2} $$

なお、$s^{2}$の係数である$a$は必ず0よりも大きくなります2のでプログラムを作るときは考慮しなくてOKです。

2次方程式$as^{2} + bs + c = 0$の判別式$D$を求めます。 $$ D = b ^ {2} - 4 a c $$

下に凸の2次関数なので、$D<0$の場合は解無しです。

$D\ge0$とし、2次方程式$as^{2} + bs + c = 0$の解を求めます。

$$ s = \frac{-b \pm \sqrt{b^{2} - 4ac}}{2a} $$

$f(s) = 0$ の2解を$s_1$, $s_2$ ( $ s_1 \le s_2 $ 、重解の場合は $ s_1 = s_2 $ )とすると、$s_1 \le 1$ かつ $0 \le s_2$ ならば $s$ が存在します。

つまり、$D\ge0$ かつ $s_1 \le 1$ かつ $0 \le s_2$ が条件となります。

プログラムを作る

長々と書きましたが、数式を作ってしまえばそのままプログラムにしてしまえば動きます。 (本当は式を変形して根号や割り算を極力減らしたほうが高速になります)

import math

def collision_detect(circle, line):
    # ベクトルの大きさの2乗を求める
    def len_vec2(vec):
        return vec[0] * vec[0] + vec[1] * vec[1]
    # 2つのベクトルの内積を求める
    def inner_prod(vec1, vec2):
        return vec1[0] * vec2[0] + vec1[1] * vec2[1]
    # 円の中心座標
    pos_o = circle[0], circle[1]
    # 円の半径
    r = circle[2]
    # 線分の端点1(点A)
    pos_a = line[0], line[1]
    # 線分の端点2(点B)
    pos_b = line[2], line[3]

    # A=Bの場合は線分でないため衝突していないとする
    if pos_a == pos_b:
        return False

    # r <= 0 の場合は円のサイズが無いため衝突していないとする
    if r <= 0:
        return False

    vec_oa = pos_a[0] - pos_o[0], pos_a[1] - pos_o[1]
    vec_ob = pos_b[0] - pos_o[0], pos_b[1] - pos_o[1]

    len_oa2 = len_vec2(vec_oa)
    len_ob2 = len_vec2(vec_ob)
    inner_ab = inner_prod(vec_oa, vec_ob)

    # ABをs : 1-s で内分した点Pが円Oに含まれる条件を立式し、整理する。
    # 2次不等式 as^2 + bs + c <= 0 と表し、係数を変数に代入。
    # ※a > 0
    a = len_oa2 + len_ob2 - 2 * inner_ab
    b = -2 * len_oa2 + 2 * inner_ab
    c = len_oa2 - r * r
    
    # 2次方程式の解の判定式
    det = b * b - 4 * a * c
    if det < 0:
        return False

    # 解の公式をとき、2解をそれぞれs1, s2に設定
    s1 = (-b - math.sqrt(det)) / (2 * a)
    s2 = (-b + math.sqrt(det)) / (2 * a)

    return (s1 <= 1) and (0 <= s2)

print(collision_detect((0, 0, 10), (10, 0, 0, 10)))
print(collision_detect((0, 0, 10), (15, 0, 0, 15)))

結果

True
False

終わりに

数学を使ってアルゴリズムを考え、プログラムにしてみました。 計算自体はプログラムにやらせてしまえばいいので、文字式を展開する必要もないです。(数学の試験ではNGかもですね)

おまけ

ちょっと発展させてみました。 f:id:s-densan:20200923202633g:plain


  1. 1次関数と円の交点を求めたほうが簡単なようです。

  2. 数学の試験ではちゃんと証明してください。

カメラの画像から線検出

概要

カメラで撮った画像から直線状のものが正しい位置にあるかどうかを判定したく、 まずPythonで直線を検出するプログラムを作成しました。

これに加え、線が目的の位置にあるかを判定する機能を作れば、目的を果たせそうです。

使用する検出法により特色があります。以下4つを切り替えられるようにしました。

  • ハフ変換
  • 確率的ハフ変換
  • LSD
  • FLD

実行イメージ

ウェブカメラで撮影した画像に、検知した直線を赤線で描画します。

f:id:s-densan:20200916212451p:plain
実行イメージ

操作方法

  • z: 次の検出方法に変更
  • a: 検出する線の最小長を増やす(検出条件を厳しくする)
  • s: 検出する線の最小長を減らす(検出条件をゆるくする)
  • ESC: 終了する

作成方法

必要ライブラリ

pipを使ったインストールコマンドです。

pip install opencv-contrib-python
pip install pylsd-nova
pip install numpy

画面描画やハフ変換・確率的ハフ変換・FLDを使用するためにOpenCVを導入します。opencv-contrib-pythonではFLDでエラーが出たので、extraが含まれるopencv-contrib-pythonとします。 LSD用にPyLSDを導入します。pylsdだとうまく行かない?よくわかりませんがフォークの一つであるpylsd-novaならうまくいきました。

ソース

from cv2 import cv2
import math
import numpy as np
from pylsd.lsd import lsd

cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()

# 確率的ハフ変換
def detect_lines_hough_p(img):
    
    # グレイスケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 線検出
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)
    lines = cv2.HoughLinesP(edges, rho=1, theta=np.pi/360, threshold=50, minLineLength=50, maxLineGap=10)

    return  [l[0] for l in lines]


# ハフ変換
def detect_lines_hough(img):
    ret_lines = []
    
    # グレイスケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    canny = cv2.Canny(gray, 50, 150, apertureSize=3)
    # 線検出
    lines = cv2.HoughLines(canny, 1, np.pi/180, 200)
    if lines is not None:
        for line in lines:
            for rho, theta in line:
                
                x1 = np.cos(theta)*rho - 1000*np.sin(theta)
                y1 = np.sin(theta)*rho + 1000*np.cos(theta)
                x2 = np.cos(theta)*rho + 1000*np.sin(theta)
                y2 = np.sin(theta)*rho - 1000*np.cos(theta)

                ret_lines.append((x1, y1, x2, y2))
        return ret_lines
    else:
        return []

# LSD
# use pylsd-nova
def detect_lines_lsd(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    linesL = lsd(gray)
    lines = [(x1, y1, x2, y2) for (x1, y1, x2, y2, width) in linesL]
    return lines


# FLD
def detect_lines_fld(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # FLDインスタンス生成
    length_threshold = 4 # 10
    distance_threshold = 1.41421356
    canny_th1 = 50.0
    canny_th2 = 50.0
    canny_aperture_size = 3
    do_merge = False

    # 線検出
    fld = cv2.ximgproc.createFastLineDetector(length_threshold,distance_threshold,
                    canny_th1,canny_th2,canny_aperture_size,do_merge)
    # ライン取得
    lines = fld.detect(gray)
    return  [l[0] for l in lines]


def main():
    # 線検出タイプ
    detect_lines_types = [
        ('FLD', detect_lines_fld ),
        ('LSD', detect_lines_lsd ),
        ('HoughLines', detect_lines_hough),
        ('HoughLinesP', detect_lines_hough_p),
    ]

    # 選択中線検出タイプ番号
    selection = 0

    width = 640
    height = 480
    # 線マスクの色
    mask_color = (0, 0, 255)
    # 線の最小長さ
    min_len = 000

    while True:
        ret, frame = cap.read()
        img = cv2.resize(frame, (width, height))
        
        if detect_lines_types[selection][1] is not None:
            lines = detect_lines_types[selection][1](frame)
            # 255で埋まった3次元配列(縦x横xRGB3次元)を生成
            mask = np.full((height, width, 3), 255, np.uint8)
            # 線の数
            cnt = 0
            for line in lines:
                x1, y1, x2, y2 = line
                # マスクに赤線を引く
                if (x1 - x2) ** 2 + (y1 - y2) ** 2 >= min_len ** 2:
                    mask = cv2.line(mask, (int(x1), int(y1)), (int(x2), int(y2)), mask_color, 3)
                    cnt += 1
            # マスクかけ
            masked_img = cv2.bitwise_and(img, mask)

            # 文字描画
            black = (0, 0, 0)
            white = (255, 255, 255)
            text1 = f'{selection+1}/{len(detect_lines_types)} {detect_lines_types[selection][0]}'
            img_with_text = cv2.putText(masked_img, text1, (1, 31), 0, 1, black)
            img_with_text = cv2.putText(masked_img, text1, (0, 30), 0, 1, white)
            img_with_text = cv2.putText(masked_img, f'line num: {cnt}, min length: {min_len}', (1, 61), 0, 1, black)
            img_with_text = cv2.putText(masked_img, f'line num: {cnt}, min length: {min_len}', (0, 60), 0, 1, white)
            img_with_text = cv2.putText(masked_img, f'z: next detection type, a: inc min line length, s:dec min line length, ESC: exit', (1, height - 20 + 1), 0, 0.5, black)
            img_with_text = cv2.putText(masked_img, f'z: next detection type, a: inc min line length, s:dec min line length, ESC: exit', (0, height - 20), 0, 0.5, white)
            cv2.imshow('masked', img_with_text)

        k = cv2.waitKey(1)
        if k == 27:
            break
        elif k == ord('z'):
            selection = (selection + 1) % len(detect_lines_types)
        elif k == ord('a'):
            min_len += 20
        elif k == ord('s'):
            min_len = max(min_len - 20, 0)

    cap.release()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    main()

簡単な解説

関数detect_*にカメラで撮影した画像を渡すと、検出した線の情報を(x1, y1, x2, y2)のタプルのリストとして返却します。

確率的ハフ変換、LSD、FLDは線分として検出し、ハフ変換は直線として検出するようです。

線分で検出するものは短い線も検出されてしまうので、ある程度の長さ以上でフィルタを掛けられるようmin_lenを用意しています。

終わりに

検出自体はほとんどライブラリ機能を呼び出しているだけなので、シンプルに実装できました。 ただしこのままだと過剰な検出がされているので、この情報から不要な情報の除外やパラメタ調整などをして使える情報に加工することが必要そうです。

さくっと使うプロジェクト管理ツール

何がしたいのか

  • プロジェクト管理ツールを使いたい
  • でもクラウドサービスは使えない(使いたくない)
  • 当然サーバなんて無い、持ちたくない

どうするのか

WindowsにOpenProjectをインストールします。

用意するもの

導入手順

コマンドプロンプトを起動。 インストールしたいフォルダに移動し、以下のコマンドを実行します。

git config --global core.autocrlf false
git clone --depth=1 --branch=stable/10 https://github.com/opf/openproject openproject
cd openproject
docker-compose up -d

エラーなく完了したら、ウェブブラウザで以下のアドレスにアクセス。

http://localhost:8080/

みんなに周知

他の人には、http://(あなたのIPアドレス):8080 を伝えてアクセスしてもらいます。 インターネット経由など、ネットワークをまたぐ場合は「さくっと」できないので省略します。

二回目以降

シャットダウンするとOpenProjectは終了してしまいます。 コマンドプロンプトでインストールしたフォルダ(openprojectという名前)に移動し、以下のコマンドを実行します。

docker-compose up -d

サーバ引っ越し(VPS調査)

はじめに

倶楽部活動の記録や情報整理のため、社内Wikiシステム「GROWI」を使っています。 今まではGoogle Cloud Platform(GCP)のサービスの一つであるGoogle Compute Engine(GCE)に構築してましたが、無料だと遅い!ということで引っ越しを検討してみました。

GROWIには、「GROWI.cloud」というレンタルサービスがありますが、勉強のためVirtual Private Server(VPS)を借りることにしました。

検討の経緯や比較結果をまとめてみます。

VPSの条件

現在使っているGCEですが、無料を維持するために以下のような制限がついていました。

  • リージョンは米国
  • マシンタイプはf1-microのみ(vCPU x 1コア、メモリ0.6GB)

よって、リージョンは日本、スペックはメモリがかつかつなのでメモリ1GB以上を条件とします。

また、円滑な移行と、費用の節約のため、時間単位の課金であること(月額課金でないこと)とします。

また、サーバ運用自動化のため、WebAPIやコマンドラインツールなどで外部からサーバ操作ができるものを条件にします。

以上+予算の条件を加えると以下の通りです。

  • リージョンは東京
  • メモリ1GB
  • 時間単位の課金
  • 外部からのサーバ操作
  • 1月あたり1,000円以下
  • 無料で試用できる

比較

候補は以下の3つに絞りました。

  • AWS Lightsail
  • Vultr
  • ConoHa

条件は全て満たしているので、その中で差が出る部分を比較しました。

No. 比較項目 LightSail Vultr ConoHa
1 外部からの操作方法 aws-cli(コマンドラインプログラム) Web API Web API
2 スナップショット価格 月額0.05ドル(USD)/GB 0円 50GBまで0円
3 月額費用(メモリ1G) $5 $5 880円
4 月額費用(メモリ2G) $10 $10 1,680円
5 スケールアップ・ダウン スナップショット経由ならば可能 可能 可能
6 試用 1ヶ月間メモリ512マシン無料 1ヶ月間$100分無料(2020年4月時点) 1ヶ月間700円分無料(2020年4月時点)

AWS Lightsail

Amazonが提供するサービスです。 大手の安心感があります。

AWSでサーバを借りると言ったらEC2が有名ですが、EC2が飲み会で言う「席だけ予約」に対して、Lightsailは「飲み放題」に当たります。 EC2は様々なサービスやリソースを組み合わせて使用できますが、Lightsailはサービスやリソースがパックになっています。 自由度が少ないですが準備や学習にかける時間が少なく、また小規模なサーバであれば割安になります。

Vultr

アメリカにあるクラウドプロバイダーですが、日本にデータセンターを持っています。 コストパフォーマンスが良いとのこと。海外では人気のようです。

クーポンを使うと、最初の1ヶ月限定ですが100ドル分の利用料がもらえます。

ConoHa

GMOが運営するVPSです。今回の条件を満たす(おそらく)唯一日本企業のサービスです。

月額費用(メモリ1G)880円で割高に見えますが、CPUが2コアで他サービスの1コアと比べて高スペックです。 なかなかよさそうです。

結果

Vultrを選びました。と言ってもどれもこれも似たりよったりで、最初の使い勝手、つまりは試用がしやすかったのが大きな理由です。

解約も初期費用も無料、使った時間だけ課金なので乗り換えは容易です。興味があったらまず初めてみるといいと思います。