eucaly が 2021年11月23日21時23分37秒 に編集
初版
タイトルの変更
RaspberryPiでLチカ(モニター同期型)
タグの変更
RaspberryPi
LED
opencv
キャプチャ
メイン画像の変更
記事種類の変更
製作品
本文の変更
# アンビエント系オサレLED まあ、モニターの裏とかにつけてさ。 画面と同期して。 LEDの色とか変わったら楽しいよねっていう。 そんなLチカ案件です。 こんなものができました。 @[youtube](https://www.youtube.com/watch?v=mrOYTVEQq8k) # もちろん既製品もあります そんなパーティーピーポー用途向けに、オランダのフィリップス様がちゃんとパッケージ製品をご用意してあったりします。 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-%E5%83%8D%E3%81%8F%E3%80%81%E3%83%95%E3%82%A1%E3%83%BC%E3%83%A0%E3%82%A6%E3%82%A7%E3%82%A2-%E3%82%A2%E3%83%88%E3%83%A2%E3%82%B9%E3%80%81DTS%EF%BC%9AX%E3%80%81%E3%83%87%E3%83%A5%E3%82%A2%E3%83%AB-%E3%82%A2%E3%82%A6%E3%83%88%E3%80%81CEC%E3%80%81%E3%82%AB%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%89%E3%80%81USB-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ピンを使っています。 ## ソフトウェア構築 まずは環境作って・・・ ```sh:setup apt install python3-opencv apt install pyhon3-pip pip3 install rpi_ws281x ``` 上記のURLを参考に、制御スクリプトを構築。 ```pyhon: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にして、かなり色補正を掛けています。 # 動作動画 こんな感じに、なかなか楽しい。 @[youtube](https://www.youtube.com/watch?v=mrOYTVEQq8k) まあ遅延が気にはなるけど、、、適当なスクリプト処理だとこんなもんでしょう。 リアルタイム性を追求するなら、それこそFPGAとかで頑張りたいところ・・・。 以上です。