PikaPikaLight

主に光モノ工作の忘備録

UNIT-VでopenMV使ってIMEモード判別してみた

f:id:PikaPikaLight:20210430213308p:plain

初めに

ブラインドタッチができない人間(私)が日々悩まされている、「日本語モードだと思って打っていたら英数モードだった(その逆もしかり)問題」の対策として「あ」or「A」を判別させるAIカメラの作成に取り掛かった。
ファーストステップとしてM5StickVのV-Trainingで狙いの動作をさせることができた。詳細は以下の記事にて。 pikapikalight.hatenadiary.com

M5StickVにはLCDとバッテリーがついていて便利なのだが、今回やりたいことに対してはこの機能は不要、むしろいらない機能といえる。 今回の目的に対してはUNIT-Vを使うのが最適なので、セカンドステップとしてUNIT-Vで同じものを作ることにした。

エッジAIの挫折

M5StickVでは割とすんなりできたので、UNIT-Vでも簡単にできると思って始めたのだが、これが予想外の苦戦を強いられる。
V-Trainingは学習曲線を見るかぎり問題なく学習できていそうなのだが、そのKモデルをUNIT-Vにいれて動かすと正答率がすこぶる悪い。カメラの位置、角度をシビアに調整してやっと正解が出るといった具合で、使い物にならないレベル。
写真の撮り方、写真サイズ、ブライトネス、コントラスト、色々変えてみたりしたが、うまくいかず。果てはV-Trainingを使わず、自力で学習してモデル作成するということもやってみたが、それでもやっぱり実機動作で正答率悪し。 そのあたりのいきさつはこちら。

結局はAIによる判別はあきらめたのですが、Google Colaboratoryを使っての自力学習の方法など非常に得たものは大きかったので、詳細は別の記事にしたいと思います。

openMVに挑戦

色々と試している過程で必然的にMaixPyの機能について調べることとなったのだが、思っていた以上にいろいろな機能があることが分かった。openMVとやらの機能が使えて、フィルターをかけたり、特定の色の領域を判別させたりと、AI使わなくてもひょっとしてできるかもと思い始めた。
さらに調べるとこのような記事を発見 マーカーシールを使ってメーターの針の位置を読み取るというもの。このマーカーシールを使う方法で今回やりたいことを実現することができた。

概要

  • マーカーシールを「あ」の真下に貼る
  • sensor.set_auto_gain(False)  でオートゲインを無効にする
  • MaixPyのしきい値エディタでマーカーのしきい値と「あ」「A」のしきい値を確認
  • .find_blobsでマーカーシールの領域を特定
  • マーカーシールの領域情報から「あ」の領域を特定し、その領域を切り出す
  • 切り出した領域に対して.find_blobsで「あ」「A」の領域サイズを判定
  • マーカーシールのサイズと「あ」「A」の領域サイズの比から「あ」を判別(「あ」のほうが明らかにサイズが大きいので区別ができる)

しきい値エディタ

MaixPyでフレームバッファにカメラの絵が出ている状態で メニューの「ツール」→「マシンビジョン」→「しきい値エディタ」→ソースイメージの場所 フレームバッファ でしきい値エディタを起動する。 スライドバーを適当に動かして、マーカーシールのみが表示されるしきい値を確認する。 f:id:PikaPikaLight:20210430213932p:plain 同じように「あ」「A」のしきい値も確認する

コード

import sensor, image, time
#import gc, math
from board import board_info
from Maix import GPIO
from fpioa_manager import fm
import sys

while 1:
    try:
        sensor.reset() #Reset sensor may failed, let's try some times
        break
    except:
        time.sleep(0.1)
        continue
sensor.set_hmirror(1)
sensor.set_vflip(1)                 # run automatically, call sensor.run(0) to stop
sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA)   # Set frame size to QVGA (320x240)
sensor.skip_frames(time = 1000)     # Wait for settings take effect.
#sensor.set_brightness(-2)
#sensor.set_contrast(-2)
sensor.set_auto_gain(False)    #これ重要。無効にしないと周りの明るさで大きく変化してしまう。
#clock = time.clock()                # Create a clock object to track the FPS.

#LED点灯用
fm.register(35, fm.fpioa.GPIOHS0, force=True)
pinout35 = GPIO(GPIO.GPIOHS0, GPIO.OUT)
pinout35.value(0)

mark_threshold  = (0, 100, -128, -28, -128, 127)    #マークシールのしきい値
text_threshold = (100, 34, -128, 127, 127, -128)    #「あ」のしきい値

while True:
    img=sensor.snapshot()
    blobs = img.find_blobs([mark_threshold])
    if blobs:
        for b in blobs:
            if b.area() > 1000:    #ゴミとりのため、小さい領域は無視する。
                #print(b)
                tmp = img.draw_rectangle(b[0:4],color=(0,255,0))
                tmp = img.draw_cross(b[5], b[6],color=(0,255,0))
                mark_x = b[0]
                mark_y = b[1]
                mark_w = b[2]
                mark_area = b.area()
                try:
                    tmp = img.draw_rectangle(mark_x,mark_y - int(mark_w*1.5),mark_w,mark_w,color=(0,0,255))  #シールの位置から文字の位置を特定
                    img2 = img.copy((mark_x,mark_y - int(mark_w*1.5),mark_w,mark_w))  #文字の領域を切り出す
                    blobs2 = img2.find_blobs([text_threshold]) #切り出した領域に対してしきい値判定
                    if blobs2:
                        for b in blobs2:
                            #print(b.area())
                            if b.area() > 200:  #小さい領域は無視する。
                                img.draw_rectangle(mark_x+b[0],mark_y - int(mark_w*1.5) + b[1],b[2],b[3],color=(255,255,255))
                                text_area = b.area()/mark_area #比を計算
                                print(text_area)
                                if text_area > 0.2:   #「あ」の判定基準。実機確認の結果より。
                                        pinout35.value(1)
                                else:
                                        pinout35.value(0)
                except:
                    pass

サードステップ

今回の実施条件は照明がほとんど一定の環境下のもの。照明条件が大きく変わるような条件、例えば直射日光が当たるような場所では正しく動かないはずである。
ホワイトバランスをとるような仕組みがあればより安定した動作ができると思う。今後の検討課題である。
また、「あ」と「A」の区別を領域のサイズ比で行ったが、この判定部分をAIにさせることでより精度の高い判定ができそうな気がする。 マシンビジョンとAIの組み合わせ、なんかカッコいいのでトライしてみたい。

【作品まとめ】M5Stackで霹靂一閃

Twitterにあげていた作品のまとめ f:id:PikaPikaLight:20210421212814j:plain


ハーフミラーを使って面白もの作れないかと考えていて思いつきました。
Yin and Yangの絵はM5Stackライブラリの中のサンプルコードより。 M5Stack\examples\Advanced\Display\Sprite\one_bit_Yin_Yang
これをベースにLovyanGFXを使って上側の絵と下側の絵を別のspriteで作成し、パカッという動きを実現させています。


クモの絵はパワーポイントに入っていた素材を使用。
WS2812を144個使用。M5Stackでは144個の制御ができなかったので、WS2812の制御とM5Stack2台へのタイミング信号の送り出しは別のマイコンで実現。
クモの絵のパカッという動きのプログラムはspriteを理解することで実現。 もしリクエストあればコード公開します。(現状、全然整理してないコードなのでとてもお見せできない)


光モノの工作を色々してきたが、ここにきて影の面白さに目覚めた。


舞台裏編



【作品まとめ】M5Stackでホログラム

Twitterにあげていた作品のまとめ
f:id:PikaPikaLight:20210418214459p:plain www.hitachi.co.jp こちらの記事を見て、M5Stackなら自分でプログラム作れば好きな絵を浮かばせられるじゃん、と思って始めました。 実際にはこれは「ホログラム」とは違うものとは承知していますが、他に呼びようないのでホログラムと言っちゃってます。


LovyanGFXの描画速度の速さが体感できます。一番早いのは30回転首振りしているはずですが全然見えません。


Wio Terminalのほうが画面サイズ大きいので向いているかもしれません。


もともとM5Stack 2台持っていましたが。これやるために2台買い増し。


このために電動ターンテーブルも購入




M5StickVでIME入力モードを判別してLチカさせてみた

f:id:PikaPikaLight:20210417084318p:plain

構想

M5StickVでパソコンのモニター画面のIMEのモード表示「あ」or「A」を判別させ、「あ」ならLEDを点灯させ、日本語モードであることがキーボードに目線を置いていても分かるようにする。ブラインドタッチができない人間(私)が日々悩まされている問題に先端技術を持って取り組む。

環境&Version

Windows10
SDカード:SanDisk 32G HC Class10
EasyLoader(V5.1.2)
VTraining-Client-VerA02B01

V-Training実施レポ

M5StickVはV-Trainingという画像判別のための至れり尽くせりなサービスがある。 M5Stack Docs - The reference docs for M5Stack products.
基本的にはこのページに従って進めていけばOK。

  1. EasyLoaderでファームウェアの書き込み

  2. VTraining-Client.zipをダウンロードして中身をSDカードに書き込み。

  3. M5StickVの電源を入れると学習用データ撮影モードのソフトが起動する。

学習には3クラス以上必要で、1クラス35枚以上の写真が必要と書いてあるので、「あ」、「A」、「×」の3種類の写真を35枚ずつ撮影。

f:id:PikaPikaLight:20210416205033p:plain
学習用の写真 3クラス
trainフォルダとvaildフォルダができるので二つのフォルダを選択した状態で(これ肝心らしい)→送る→圧縮(zip形式)フォルダ でzipファイルを作成。
それをデータアップロードページでアップロード
http://v-training.m5stack.com/
しばらく待つとメールで結果が返ってくる。

が、1回目の結果は

[V-Trainer] *** Online Training Request Failed
Hi! Sorry for that, there is some error happened during the training process. We attached the reason below, if you have any questions, welcome to report to us through Twitter, Facebook, or Forum. Or, check out our docs here: https://docs.m5stack.com/#/en/related_documents/v-training
USERID: *** CONTENT: Lake of Enough Train Dataset, Only 78 pictures found, but you need 90 in total.

Notice:
Image 16.jpg was removed for being too close to 82.jpg, hashdiff: 2
Image 93.jpg was removed for being too close to 82.jpg, hashdiff: 2
・・・

自分のつたない英語力でも読み取れます。「90個以上の写真がないとだめだけど君のは78個しかなかったよ。ちなみに似た画像があったから消しちゃったよ。 」
なるほど。似た画像になってしまうのは避けられないので、これはいっぱい写真を撮るしかない、ということで2回目は各100枚の写真を撮って、再チャレンジ。 今度は成功。

[V-Trainer] *** Online Training Request Finished
Model: Classification MobileNetV1 Alpha: 0.7 Depth: 1 f:id:PikaPikaLight:20210416210949p:plain

学習曲線がついていて、学習がうまくいったのか分かる仕組み。これが無料でつかえるとは有り難い。
メールのURLから結果ファイルをダウンロードして、中身をSDカードに書きこめばOK
SDカード直下のboot.pyが起動時に実行されるようなので、最初の撮影用のファイル一式は別フォルダ作って移動させておいた。  

起動して、パソコン画面にカメラを向けると、「おお!判別している」 (文字ちっちゃ ) けど正答率はいまいち。
手持ち撮影による不安定さが原因かもしれないと思い、今度はカメラを台座に固定して、しっかり位置キープした状態で再々チャレンジ。

初めてのPython

M5StickV、ボタン押しにくい。台座に固定した状態ではなおさら。なので1回押したら100回連続で撮影するようにプログラムを修正することにした。

元のコード のこの部分に

try:
    while(True):
        img = sensor.snapshot()
        if but_a.value() == 0 and isButtonPressedA == 0:
            if currentImage <= 30 or currentImage > 35:

for文を追加

try:
    while(True):
        img = sensor.snapshot()
        if but_a.value() == 0 and isButtonPressedA == 0:
            for num in range(100):
                if currentImage <= 30 or currentImage > 35:

生まれて初めてのPythonプログラミング 。コンパイルとかせずに、ただ書いたコードをSDに入れるだけで動くというのも驚きの仕組み。
ドキドキしながらシャッター(Aボタン)オン → 画面固まる → カチャ音100回 → 同じ写真が100枚取れる。

forループの中に  img = sensor.snapshot() を入れてなかった。サウンドも消してなかったので延々カチャカチャ。
それでもエラーではなかったので、気を取り直して修正。
validationデータが31-35の5枚だけなのもよくないかと思い、5枚に1枚をvalidationデータにするように修正。

try:
    while(True):
        img = sensor.snapshot()

        if but_a.value() == 0 and isButtonPressedA == 0:
            for num in range(100):  #100回撮影
                time.sleep(0.1)  #100ms delay入れておく。
                img = sensor.snapshot()
                #if currentImage <= 30 or currentImage > 35:
                if currentImage % 5 != 0: #5枚に1枚をvalidationデータにする
                #以下略。play_sound("/sd/kacha.wav")はコメントアウト

再々チャレンジの結果はまずまず。カメラ位置の調整はしないといけないが、ちゃんと3クラスの判別ができている。
こんなに簡単にAIカメラが作れるとは驚き。

試しているとフリーズする現象が発生。調子いい時もあるが、だいたい途中でフリーズする。USBシリアルでログを見てみると pmax=max(plist) のところで型エラーが起きているみたい。 Pythonの型が全く分かっていないのでエラーを読み解けない。とりあえず覚えたての必殺技 try: except: pass でエラー起きても無視させてみる。結果、フリーズすることはなくなった。こんなんでいいのかと思いながらも先に進む。

あとは「あ」の時にLEDを光らせる仕組みを入れる。
M5StickV自身にもLEDはついているがカメラ側についているのでこれは使えない。使えるピンはGroveコネクタのところだけなので、ここを使うしかない。LED光らせるだけなら直接ドライブさせてもいいと思うが、手持ちにLEDドライバの基板があったのでこれを利用。コード的には直接ドライブと一緒。
あとLCDの画面は位置合わせの時には便利だが、常時ついていると気になるので、ボタンを押したら消せるようにしてみた。

最終的なコードがこれ(初めてのPython

import image
import lcd
import sensor
import sys
import time
import KPU as kpu
from fpioa_manager import *
from board import board_info
import KPU as kpu
from Maix import GPIO

fm.register(board_info.BUTTON_A, fm.fpioa.GPIO1)
but_a=GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_UP) #PULL_UP is required here!

fm.register(35, fm.fpioa.GPIOHS1, force=True)
pinout35 = GPIO(GPIO.GPIOHS1, GPIO.OUT)
pinout35.value(0)

lcd.init()
lcd.rotation(2)

try:
    from pmu import axp192
    pmu = axp192()
    pmu.enablePMICSleepMode(True)
except:
    pass

try:
    img = image.Image("/sd/startup.jpg")
    lcd.display(img)
except:
    lcd.draw_string(lcd.width()//2-100,lcd.height()//2-4, "Error: Cannot find start.jpg", lcd.WHITE, lcd.RED)

task = kpu.load("/sd/XXX_mbnet10_quant.kmodel")

labels=["1","2","3"] #You can check the numbers here to real names.

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.run(1)

lcd.clear()

isButtonPressedA = 0
off_flg = 0

while(True):
    try:
        img = sensor.snapshot()
        fmap = kpu.forward(task, img)
        plist=fmap[:]
        pmax=max(plist)
        max_index=plist.index(pmax)
        if pmax > 0.95:
            # lcd.draw_string(40, 60, "Accu:%.2f Type:%s"%(pmax, labels[max_index].strip()))
            print(labels[max_index])
            if max_index == 0:
                pinout35.value(1)
            else:
                pinout35.value(0)
        if but_a.value() == 0 and isButtonPressedA == 0:
            isButtonPressedA = 1
            if off_flg == 0:
                lcd.set_backlight(False)
                lcd.clear()
                off_flg = 1
            else:
                lcd.set_backlight(True)
                lcd.clear()
                off_flg = 0
        if but_a.value() == 1:
            isButtonPressedA = 0
        if  off_flg == 0:
            a = lcd.display(img)
    except:
        pass
a = kpu.deinit(task)

ファーストステップ完成

セカンドステップ

M5StickVにはLCDとバッテリーがついていて便利なのだが、今回やりたいことに対してはこの機能は不要、むしろいらない機能といえる。
今回の目的に対してはUNIT-Vを使うのが最適なので、UNIT-Vで同じものを作ることにする。 2021/4/16現在、取組中。学習はいい結果が出せているが、モデルをUNIT-Vに入れて判別動作をさせると正答率がかなり悪いという問題で苦闘中。
その結果はまた記事にしたいと思います。

【作品まとめ】M5Stackで万華鏡

f:id:PikaPikaLight:20210506210159p:plain Twitterにあげていた作品のまとめ


ヤマザキミノリ先生のCUMOSを意識した作品です。 Twitterではガラスブロックと言ってますが多分アクリルの間違いです。キャン★ドゥで購入したものですが現在見かけません。買いだめしておけばよかった。


LovyanGFXの描画が凄すぎて、あえてボールの数を減らしてます


カラーだとめまいを起こしそうなくらい激しかったので白黒に変換して投稿


モカワイさがお気に入りで、このシリーズは何種類も作りました。





M5Stack プロトモジュールの基板データを開いて寸法を測ってみた。

本記事は2021/4/12の情報です。
OS: Windows10

初めに

M5Stackのアクセサリにプロトモジュールという拡張用のボードがあります。 www.switch-science.com しかしながらこのボード、MBUSコネクタから配線が引き出されていないため、コネクタの足に直接ワイヤーをはんだ付けして配線するという、若干残念な代物です。基板設計できる人であればボードを自作したくなります。
基板自作にあたって、一番必要なのはMBUSコネクタの位置寸法ですが、公式にはこの寸法図はないようです。

基板データ発見?

M5StackのGitHubを散策していてそれらしいデータを見つけました。
GitHub - m5stack/M5-3D_and_PCB
ここに PROTO.stl と PROTO_254.PcbDoc というファイルがあります。 PROTO.stlはプロトモジュールの筐体のデータでした。
PROTO_254.PcbDoc はボードのCADデータである可能性高そうです。 拡張子からAltiumのデータだと見当つきます。実は私、基板CADには詳しく、基板設計だけでなくCADのシステム管理などしていたこともあります。Altiumもちょっとだけ使ったことがあります。

Altium Designer でデータ開いてみる

Altium のホームページ https://www.altium.com/jp でAltium Designerビューワーの無償ライセンス(有効期間6か月)を手に入れます。
名前、電話番号、メールアドレス等を登録すると、すぐにメールが届きます。メールにダウンロードのURLとライセンスの設定方法など記載されているので、その通りに進めれば簡単にセットアップできました。特に詰まるところはありませんでした。
データを開いてみると、

f:id:PikaPikaLight:20210412193957j:plain
実物と比較の図
どうやら当たりのようです。

主要な寸法を測定

f:id:PikaPikaLight:20210412194402p:plain
外形
f:id:PikaPikaLight:20210412194434p:plain
四隅の穴
f:id:PikaPikaLight:20210412194528p:plain
MBUSコネクタの位置決め穴
MBUSコネクタの下にあるので分かりにくいですが、位置固定用のボス穴があります。これ入れておかないとコネクタの実装するときに位置決めるの大変なので、自作する場合忘れずに入れましょう。
f:id:PikaPikaLight:20210412194604p:plain
MBUSコネクタ
f:id:PikaPikaLight:20210412194658p:plain
MBUSコネクタのパッド

虹を投影する懐中電灯を作った

2021/4/7 追記(サンプルコード、紫色について)

初めに

以前からLEDを使った光モノの工作をしていました。 前々から一つの目標だったものに「虹を投影する懐中電灯」がありました。 今回それなりにイメージに近いものが作れたのでその作り方を紹介します。

注意

  • 使用しているLEDボードの光量は強烈です。プログラムで光量を設定しますので、強くしすぎないようにしましょう。LED直視は厳禁です。
  • 市販の懐中電灯を改造しています。もし同じことされる場合は自己責任でお願いします。

きっかけ

イデアのきっかけはRGB LEDテープを使って7色の影を作っていた時でした。 このとき指と指の隙間の影が虹の形になっていることに気づきました。
そこで思いついたのが虹色の順にLEDを並べてスリットを通すという方法です。

このツイートの写真には載せていませんでしたが、これはRGB LED16個を虹色の並び(紫→赤)で点灯させ、ある程度の距離においた型紙に虹型のスリットを設けてそこを通して投影したものです。
とても幅広ですが虹の形と色で光を投影することができました。

原理

スリットを通すことで虹型が作れる理由はこんな感じだと思います。

f:id:PikaPikaLight:20210406211606p:plain
LEDの光は放射状に照射されますが、スリットを通すことである程度直進させることができます。赤色のLEDを下側に紫色のLEDを上側に配置してスリットを通すと、赤の光は下から上の方向にスリットを通るので上側が赤くなります。同じように紫色の光は上から下方向にスリットを通るので下側が紫になります。このように各色の光をスリットを通すことで虹の形と色を再現することができました。

試作

RGB LEDテープを使ったのではどうしても幅広になってしまうのでコンパクトに7色並べられる方法を考えていた時に目についたのがM5StackのHEX RGB LED Board です。 www.switch-science.com これはちょうど7列あって、サイズもちょうど懐中電灯に収まりそう。
100円ショップで大きめの懐中電灯を買ってきました。単3電池3本使用で4.5Vですが、LED点灯させることできました。
コントローラーに極小のM5Atom-Liteを使うことで懐中電灯の頭の部分に収めることができました。
各LEDの色は固定なのでサンプルコードをベースにすれば簡単にプログラムできます。

void show_rainbow(void) {

  float zz = 2.0;  //明るさの調整用

  for (int i = 0; i < 4; i++) { // violet
    pixels.setPixelColor(i, pixels.Color(125 / zz, 0, 255 / zz));
  }

  for (int i = 4; i < 9; i++) { // indigo
    pixels.setPixelColor(i, pixels.Color(50 / zz, 0, 255 / zz));
  }

  for (int i = 9; i < 15; i++) { // blue
    pixels.setPixelColor(i, pixels.Color(0, 0, 255 / zz));
  }

  for (int i = 15; i < 22; i++) { // green
    pixels.setPixelColor(i, pixels.Color(0, 255 / zz, 0));
  }

  for (int i = 22; i < 28; i++) { // yellow
    pixels.setPixelColor(i, pixels.Color(255 / zz, 255 / zz, 0));
  }

  for (int i = 28; i < 33; i++) { // orange
    pixels.setPixelColor(i, pixels.Color(255 / zz, 100 / zz, 0));
  }

  for (int i = 33; i < 37; i++) { // red
    pixels.setPixelColor(i, pixels.Color(255 / zz, 0, 0));
  }

  pixels.show();   // Send the updated pixel colors to the hardware.
  delay(100);
}

配線がごちゃつくのを防ぐために、このようにLEDボードの拡張コネクタのほうに電源を接続し、そこからM5Atomのほうに給電する方法を考えました。 色の並びは先ほど説明したように赤色が下。上側が紫です。
f:id:PikaPikaLight:20210406221139p:plain これにある程度の距離をとって虹型のスリットを配置します。 大き目の紙コップがジャストサイズでこれを利用しました。 スリットのサイズは半径5センチくらいの半円に1センチくらいのスリット幅にしました。この辺は検討の余地ありです。 f:id:PikaPikaLight:20210406220951p:plain

スリットなしだと色が混ざってしまって白色になってしまいますが、スリットを通すことで虹の形と色を再現することができました。 f:id:PikaPikaLight:20210406222105p:plain

紫色について

虹の一番下の色は紫色です。これは紫色の波長が一番短いことを示しています。 ところが、RGB LEDで紫色を作る場合、波長の短い青と波長の長い赤を同時に点灯することで、紫色に見えます。
これって不思議ですね。このことに関してはまた別の機会に語ってみたいと思います。

今後の構想

今回は37個のRGB LEDボードを使用していますが、それぞれの色は固定で使っているので、単純に7色のLEDを並べれば同じことができるはずです。
もっと言えば、白色の光源を7色のラインが並んだフィルターに通してからスリットに通せばいけるはずです。
そこまでコストダウンできれば将来100円ショップとかで売られる日も夢ではないかもしれません。 子供向けの工作教室でも作れるくらい、より手軽に作れる方法を考えてみたいと思います。
また今回レンズ等の光学部品は使用していません。なのでLED自体の光量はかなり明るいですが、スリットを通った後の光はわずかで、暗いとことでないとちゃんと見えません。100円ショップで入手できるようなものをレンズ代わりに使ってもっと明るさを出せないかも検討してみたいです。 f:id:PikaPikaLight:20210407080205p:plain

最後に

ブログを始めようと思ったのは、尊敬する人気ブロガーからあげ先生が書籍の中でアウトプットをすることが大事だと言われていたことに共感したからです。ツイッターは最近始めてみたのですが、そこではここまで詳しい解説はできないし、つぶやきはすぐに流されていくものなので、記事として残したいものはちゃんと書きたいなと。
初めてのはてなブログの記事作成で、Markdown記法から勉強したり、思ったより苦労しました。 けれど、これまで一方的に多くの方のブログ記事に助けられてきたので、自分も楽しいことを共有できればと思い、何とか書き上げました(晩酌の時間削って^^)
万一この虹投影の仕組みが特許化とかされて自由に使えなくなるとかは嫌なので、こうして記事にして公知にしておきたかった、という思いもあります。