初めに
昨年末にLEDドット絵の世界に足を踏み入れました。
pikapikalight.hatenadiary.com
ドット絵のデータはExcelを駆使してそれなりに効率化できたのですが、それでもなかなか大変な作業でした。
カメラで撮った絵をリアルタイムでドット絵に変換できたら簡単にドット絵が作れて楽しそうと思い、挑戦してみました。
完成品
結論を先に。こんなのを作ることが出来ました。 youtu.be
ハードウェア
カメラはM5StackのUNIT-Vを使いました。以前こいつで色々やっていて、UNIT-VのMicroPythonはWS2812が動かせることを覚えていたのです。
pikapikalight.hatenadiary.com
ここで問題が一つ、UNIT-VのGroveコネクタは5V出せるのですが電流はほとんど流せない。とてもじゃないけど16x16=256個のLED点けることはできない。LEDパネルとUNIT-Vの電源を別々にとるしかないけれど、それだと配線がすっきりしない。
考えた末に閃きました。以前作成した「虹を投影する懐中電灯」と同じく、パネルの方に先に電源をつないでそこからコントローラーの方に電源をつなぐことで配線をすっきりさせる手法です。
電源コネクタにはUSB-Cを、UNIT-Vとの接続にはGroveコネクタを使えるように改造しました。
ソフトウェア
恥ずかしながらコードを公開します。
私はこれまでほとんどC言語しか使ったことがなく、このUNIT-Vで初めてPythonにふれました。正直、言語としての良さは理解できてません。
けれどたったこれだけのコードでこれが作れる事実はすごいなと思います。
import sensor, image, time, lcd from Maix import GPIO from modules import ws2812 from fpioa_manager import * fm.register(board_info.CONNEXT_A) class_ws2812 = ws2812(board_info.CONNEXT_A,256) while 1: try: sensor.reset() break except: time.sleep(0.1) continue sensor.set_hmirror(1) sensor.set_vflip(1) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.B64X64) sensor.skip_frames(time = 1000) sensor.set_contrast(-2) sensor.set_saturation(2) sensor.set_auto_gain(True) sensor.run(1) r_num = 0 g_num = 0 b_num = 0 for i in range(10): a = class_ws2812.set_led(i,(10,10,10)) a=class_ws2812.display() img = sensor.snapshot() img2 = img.resize(1, 1) print(img2.get_pixel(0, 0)) r_num = r_num + img2.get_pixel(0,0)[0] g_num = g_num + img2.get_pixel(0,0)[1] b_num = b_num + img2.get_pixel(0,0)[2] time.sleep(0.1) print(r_num) print(g_num) print(b_num) rgb_ave = (r_num + g_num + b_num) / 3 print(rgb_ave) r_factor = rgb_ave / r_num g_factor = rgb_ave / g_num b_factor = rgb_ave / b_num print(r_factor) print(g_factor) print(b_factor) sensor.set_auto_gain(False,sensor.get_gain_db()) print(sensor.get_gain_db()) try: while(True): img = sensor.snapshot() img2 = img.resize(16, 16) img2 = img2.histeq() k = 0 sc = 5 for i in range(8): for j in range(16): rr = img2.get_pixel(i * 2, j)[0] * r_factor gg = img2.get_pixel(i * 2, j)[1] * g_factor bb = img2.get_pixel(i * 2, j)[2] * b_factor rr = int(rr) / sc gg = int(gg) / sc bb = int(bb) / sc rgb = (int(rr),int(gg),int(bb)) a = class_ws2812.set_led(k,rgb) k = k + 1 for l in range(16): rr = img2.get_pixel(i * 2 + 1, 15 - l)[0] * r_factor gg = img2.get_pixel(i * 2 + 1, 15 - l)[1] * g_factor bb = img2.get_pixel(i * 2 + 1, 15 - l)[2] * b_factor rr = int(rr) / sc gg = int(gg) / sc bb = int(bb) / sc rgb = (int(rr),int(gg),int(bb)) a = class_ws2812.set_led(k,rgb) k = k + 1 a=class_ws2812.display() time.sleep(0.1) except KeyboardInterrupt: pass
コードのポイント
sensor.set_framesize(sensor.B64X64)
framesizeを正方形にしておくことで16x16に変換したときに歪まないようになります。
sensor.set_contrast(-2) sensor.set_saturation(2)
こうしたほうがなんとなく色がくっきりするような気がしたのでこうしてますが、気のせいである可能性大です。
r_num = 0 g_num = 0 b_num = 0 for i in range(10): a = class_ws2812.set_led(i,(10,10,10)) a=class_ws2812.display() img = sensor.snapshot() img2 = img.resize(1, 1) print(img2.get_pixel(0, 0)) r_num = r_num + img2.get_pixel(0,0)[0] g_num = g_num + img2.get_pixel(0,0)[1] b_num = b_num + img2.get_pixel(0,0)[2] time.sleep(0.1) print(r_num) print(g_num) print(b_num) rgb_ave = (r_num + g_num + b_num) / 3 print(rgb_ave) r_factor = rgb_ave / r_num g_factor = rgb_ave / g_num b_factor = rgb_ave / b_num
起動時にホワイトバランスを補正するシーケンスです。起動時に白いものにカメラを向けて補正値を算出することで色の再現性を上げられないかと考えたコードです。本当は16x16個分の補正値を出した方がいいんでしょうが、そこまでやってません。 img.resize(1, 1)で1画素データにしてしまってそのRGBの補正値を出しています。この辺、今後の検討課題です。
sensor.set_auto_gain(False,sensor.get_gain_db())
auto_gainをFalseにするのは必須です。これをしないとチカチカしちゃいます。
img2 = img.resize(16, 16)
カメラの画像を16x16にするたった一行の呪文です。
k = 0 sc = 5 for i in range(8): for j in range(16): rr = img2.get_pixel(i * 2, j)[0] * r_factor gg = img2.get_pixel(i * 2, j)[1] * g_factor bb = img2.get_pixel(i * 2, j)[2] * b_factor rr = int(rr) / sc gg = int(gg) / sc bb = int(bb) / sc rgb = (int(rr),int(gg),int(bb)) a = class_ws2812.set_led(k,rgb) k = k + 1 for l in range(16): rr = img2.get_pixel(i * 2 + 1, 15 - l)[0] * r_factor gg = img2.get_pixel(i * 2 + 1, 15 - l)[1] * g_factor bb = img2.get_pixel(i * 2 + 1, 15 - l)[2] * b_factor rr = int(rr) / sc gg = int(gg) / sc bb = int(bb) / sc rgb = (int(rr),int(gg),int(bb)) a = class_ws2812.set_led(k,rgb) k = k + 1
sc = 5 はRGB値を5分の一に下げて、明るさ=消費電流を下げる処置です。 16個ごとに逆転させているのは使用したLEDパネルがそういう並びのものだったからです。
完成
スナフキンのフィギュアがいい感じに出たときは感動しました。