eucalyのアイコン画像

RaspberryPiでLチカ(モニター同期型)

eucaly 2021年11月23日に作成  (2021年11月23日に更新)

RaspberryPiでLチカ(モニター同期型)

アンビエント系オサレLED

まあ、モニターの裏とかにつけてさ。
画面と同期して。
LEDの色とか変わったら楽しいよねっていう。
そんなLチカ案件です。

こんなものができました。

ここに動画が表示されます

もちろん既製品もあります

そんなパーティーピーポー用途向けに、オランダのフィリップス様がちゃんとパッケージ製品をご用意してあったりします。
Hueシリーズの、「Philips Hue Play HDMI Sync Box」、コイツ。
https://www.philips-hue.com/ja-jp/p/hue-play-hdmi-sync-box-/8719514268333

まあでもコレ、一式揃えると5万くらいかかったりするし。
ちょっとねえ、、、高いのよね。

つーわけで、DIYしてみようかなと。

最初はFPGA使う気満々でした

elchikaさんのアワードで、「ARTY-Z7」というFPGAボードをライセンス付きで貰ってしまったので、最初はこいつ使う予定だったのですが。
IPが有料だったり開発環境がごそっと変わってたり最新の開発環境だとせっかくのDVI/TMDSなIPが死んでしまったり死んだ状態で回路書いてしまってデモ環境を復旧出来なかったりインストールに開発環境のセットアップに300GBくらいディスクスペース要求されたり、と、なかなか難航というか無理じゃないかこれ?になってるので。

ラズパイでサクっと作ってみることに。

作戦と留意事項

HDMI動画の取り込み

ラズパイにHDMI動画を取り込ませる必要があります。
おおまかにやりくちは2種類あって。
USB Video Captureデバイスを、USBにくっつける、か
ラズパイについてるカメラ入力、MIPI CSI-2にHDMI変換して突っ込むか、です。
後者は東芝の変換IC付きのが数千円で売ってるのですが、この変換IC、1080p/60に対応していないので。
今回はUSB経由にすることに

HDMI画像のスケール

オイラはパソコン環境を、4Kで組んでいます。
このままだと、まあラズパイに突っ込むには大分しんどい事態になるので。
4K画像を1080にダウンスケールしてくれる、HDMIスケーラーを探していました。
すると、HDMIスプリッタにスケーラー付きという、スーパーステキデバイスがあることが判明!。
https://www.amazon.co.jp/EZCOO-働く、ファームウェア-アトモス、DTS:X、デュアル-アウト、CEC、カスケード、USB-EZ-SP12H2/dp/B07TC5C9J9

いえーあ、素晴らしい!。

Lチカのやりざま

アドレサブルLEDか、DMX制御かで悩んだんですが。
DMX用の機材到着が遅れてるので。
とりあえずアドレサブルLEDを使うことにしましたとさ。

用意したもの

  • 適当なラズパイ
  • HDMIスプリッタ(スケーラー機能つき)
  • HDMI-UVCビデオキャプチャ
  • アドレサブルLEDなLEDストリップ

構築

参考URL

このへん参考にして、頑張りました。

https://qiita.com/kakinaguru_zo/items/eda129635816ad871e9d

https://dev.classmethod.jp/articles/raspberry-pi-tape-led-ws2815/

https://pystyle.info/opencv-tone-transform/

ハードウェア構築

HDMI周り機材は、こんな感じです
思いついて半日で機材届くのステキね!、ビバお急ぎ便。
キャプションを入力できます

中身はこんな感じ。
キャプションを入力できます

適当にラズパイと接続してあげます、USBは2で十分でした。
キャプションを入力できます

アドレサブルLEDは、こんなものを調達。
キャプションを入力できます
37粒です。

ま、テストなので、適当に接続。
キャプションを入力できます
GPIOは21ピンを使っています。

ソフトウェア構築

まずは環境作って・・・

setup

apt install python3-opencv apt install pyhon3-pip pip3 install rpi_ws281x

上記のURLを参考に、制御スクリプトを構築。

ambtest.py

import time import cv2 import numpy as np from rpi_ws281x import Color, PixelStrip LED_COUNT = 37 # Number of LED pixels. LED_PIN = 21 # GPIO pin connected to the pixels (must support PWM!). LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) LED_DMA = 10 # DMA channel to use for generating signal (try 10) LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) LED_CHANNEL = 0 LED_STATE_OFF = Color(0, 0, 0) # OFF gamma = 10 x = np.arange(256) y = (x / 255) ** gamma * 255 class Ws281x: def __init__(self): self.__strip = PixelStrip( LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, ) self.__strip.begin() def on(self, red: int, green: int, blue: int) -> None: color = Color(red, green, blue) for i in range(self.__strip.numPixels()): self.__strip.setPixelColor(i, color) self.__strip.show() def off(self) -> None: for i in range(self.__strip.numPixels()): self.__strip.setPixelColor(i, LED_STATE_OFF) self.__strip.show try: capture = cv2.VideoCapture(0, cv2.CAP_V4L2) except TypeError: capture = cv2.VideoCapture(0) if capture.isOpened() is False: raise IOError #if isinstance(capture.get(cv2.CAP_PROP_CONVERT_RGB), float): # capture.set(cv2.CAP_PROP_CONVERT_RGB, 0.0) #else: # capture.set(cv2.CAP_PROP_CONVERT_RGB, False) capture.set(cv2.CAP_PROP_BUFFERSIZE, 4) capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y', 'U', 'Y', 'V')) capture.set(cv2.CAP_PROP_FRAME_WIDTH, 720) capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) capture.set(cv2.CAP_PROP_FPS, 30) last_capture_start_time = time.time() led = Ws281x() while(True): try: capture_start_time = time.time() ret, frame = capture.read() if ret is False: raise IOError capture_end_time = time.time() # print("Capture FPS = ", 1.0 / (capture_end_time- capture_start_time)) # cv2.imshow('frame',cv2.cvtColor(frame, cv2.COLOR_YUV2BGR_YUYV)) # cv2.imwrite('/ramdisk/test.jpg',frame) dstframe = cv2.LUT(frame,y) bgr = cv2.mean(dstframe) led.on(int(bgr[2]), int(bgr[1]), int(bgr[0])) print ('R:%6.2f G:%6.2f B:%6.2f' % ( bgr[2], bgr[1], bgr[0])) cv2.waitKey(1) # print("Real FPS = ", 1.0 / (time.time() - last_capture_start_time)) last_capture_start_time = capture_start_time except KeyboardInterrupt: # 終わるときは CTRL + C を押す break capture.release()

まあ、動画をキャプチャして。
フレームの画像に対して、平均値を算出して。
算出値をLEDに食わせてるだけです。
実際動かしてみたら、色をかなり強調しないと白っぽくなってしまうので。
ガンマを10にして、かなり色補正を掛けています。

動作動画

こんな感じに、なかなか楽しい。

ここに動画が表示されます

まあ遅延が気にはなるけど、、、適当なスクリプト処理だとこんなもんでしょう。
リアルタイム性を追求するなら、それこそFPGAとかで頑張りたいところ・・・。

以上です。

eucalyのアイコン画像
いつも、てきとうです
  • eucaly さんが 2021/11/23 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する