※なにか気になる点がありましたらコメント欄にご記入ください。また、工作や回路を製作する場合には、細かい作業などに対して、細心の注意を払われるようお願いいたします。
1.はじめに
以前、筆者は以下の記事で独自のやり方で、虹色をNeopixelに描いてみました。
tanuki-bayashin.hatenablog.com
しかし、最近眼にしたYoutubeの動画を見ていたら、「DHAの電子工作教室」というチャンネルで、HSVという色の表現方法があることが紹介されていました。(以下参照)
このHSVという表現形式によると、色の別の見方が分かることを知りました。
今回は筆者が独自で作った色について、この動画の内容をもとに検証してみたので、書いていきたいと思います。
2.HSVについて
簡単にいうと、色相(Hue)、彩度(Saturation)と明度(Value)の3つの値で色を示す表現方法です。(頭文字を取ってHSVとなります)
色相は色の種類、彩度は色の鮮やかさ、明度は明るさの度合いを示しているようです。(詳しくは先ほどの動画をご覧ください)
ちなみに、色相は色の違いを0°~360°の値で示します。赤が0°(=360°)、緑が120°、青が240°の辺りで、その間が中間色となります。
また、彩度は0~255で示し、0のときはモノクロの状態で、255になると鮮やかとなる感じです。
さらに明度も0~255で示し、0なら真っ黒、255なら真っ白となります。(その間だといずれかの色を示します)
※彩度と明度に関しては、0~1で表したり、100%表示するなどいろいろあるようです。
そして動画の中では、RGB(赤、緑、青)で表現された色を、HSVに変換する関数が紹介されていました。(少し修正させていただきました)
それにより、以下のような検証を行いました。
- それぞれの色の色相を調べる。(ちゃんと様々な色を表現できているか)
- 彩度を調べる(どれくらいの鮮やかさなのか)
- 明度を調べる。(明度は一定になっているか)
HSVに変換すれば、1度ですべての値が求まるので、実際の作業は楽でした。
3.検証結果
検証に用いたソースコードを以下に示します。
まず筆者独自の方法で色を作り、それをHSVの値に変換します。その後、その数値から、検証を行います。
※ライブラリcvt_col.pyは 1.はじめに の「DHAの電子工作教室」さんが制作されたものです。
下のURLの動画の説明欄(?)にソースコードへのリンク先があります。
#14-4 HSVでフルカラーLED制御 ~DHAの電子工作教室~【Raspberry Pi Pico】 - YouTube
リスト1 検証に用いたソーズコード
import cvt_col import matplotlib.pyplot as plt #各種配列を用意します col = [] hsv = [] time = [] ss = [] hh = [] vv = [] # カラフルな色を作るのに必要な関数です def triangle(x): if x < 20.0: y = int(1.5 * x) elif x < 40.0: y = 60 - int(1.5 * x) else: y = 0 return y #60色の色を生成しています for i in range(60): p = float(i) x = (p + 20.0) % 60.0 y = p z = (p + 40.0) % 60.0 r = int(triangle(x)) g = int(triangle(y)) b = int(triangle(z)) col.append([r,g,b]) #「DHAの電子工作教室」さんによるrgbからhsvを求める関数です (h, s, v) = cvt_col.rgb_to_hsv( r, g, b ) hsv.append([h,s,v]) time.append(i) hh.append(h) ss.append(s) vv.append(v) #=========================================================== #Draw graph #=========================================================== #色相のグラフ plt.title('Graph of Heu') plt.plot(time, hh, color="red", lw=2) plt.show() #彩度のグラフ plt.title('Graph of Saturation') plt.plot(time, ss, color="green", lw=2) plt.show() #明度のグラフ plt.title('Graph of Value') plt.plot(time, vv, color="blue", lw=2) plt.show()
このソースコードにより、色相、彩度、明度のグラフを描き、どのような具合なのかを調べました。
※横軸はあえて言えば、色の番号みたいなものです。
グラフ1を見てみると、色相が0°から360°に向かって増加しているのが分かります。いくらか波打っていますが、おおよそ様々な色を彩っているのが分かります。
グラフ2を見ると、見事なまでに彩度が255と最高値を取っていることが分かります。(数値でも確認しました)
特に知識があった訳ではないのですが、偶然とは言え、鮮やかな色で光らせていたことになるので、嬉しいです。
グラフ3を見ると、明度(V)の変化の様子が分かります。30と15の間を行ったり来たりしています。
色相と比較すると、赤、緑、青の色の辺りで高い値を取り、黄色やマゼンタなどの中間色で低い値を取っています。
明度は15から30と2倍の違いがあることが分かりました。調べてみないと分からないものです。
4.明度を一定にした場合
ライブラリcvt_col内の関数では明度を一定にしてrgbの値を求めることもできるので、それを用いてNeopixelを光らせることを行ってみます。(DHAさんの動画の中でも紹介されていました)
色を作る部分は以下のようになります。
リスト2 明度を一定にして虹色を光らせるコード
#必用なライブラリをインポートします import array, utime, math from machine import Pin import rp2 from rp2 import PIO, StateMachine, asm_pio import cvt_col #WS2812 のLEDの数を指定します NUM_LEDS = 100 # Pico特有のstate machine を用いてWS2812の制御を行っています #このあたりの処理はリスト1の下のリンク先の図書より引用しました # PIO State Machine to display ws2812 @asm_pio(sideset_init=PIO.OUT_LOW, out_shiftdir=PIO.SHIFT_LEFT, autopull=True,\ pull_thresh=24) def ws2812(): T1 = 2 T2 = 5 T3 = 3 label("bitloop") out(x, 1) .side(0) [T3 - 1] jmp(not_x, "do_zero") .side(1) [T1 - 1] jmp("bitloop") .side(1) [T2 - 1] label("do_zero") nop() .side(0) [T2 - 1] # Create the StateMachine with the ws2812 program, outputting on Pin(16)(). sm = StateMachine(0, ws2812, freq=8000000, sideset_base=Pin(16)) # start the StateMachine, it will wait for data on its FIFO. sm.active(1) # Display a pattern on the LEDs via an array of LED RGB values. ar = array.array("I", [0 for _ in range(NUM_LEDS)]) #position の位置のLEDをred, green, blue の値にて指定します def ar_color(position, red, green, blue): ar[position] = (green<<16) + (red<<8) + blue #全てのLEDを消灯する関数です def clear_all(): for i in range(NUM_LEDS): ar[i] = 0 sm.put(ar,8) # 3個のリストのメモリーを確保しています red = [0.0 for i in range(NUM_LEDS)] green = [0.0 for i in range(NUM_LEDS)] blue = [0.0 for i in range(NUM_LEDS)] color = [] index = [] #ここから処理がスタートします if __name__ == '__main__': # Process arguments print('Press Ctrl-C to quit.') # ここで色のテーブルを作っています。(colorというリストに値を代入しています) # 色の数は6°おきに60色、彩度は255、明度は検証の結果から23としました for i in range(60): (r, g, b) = cvt_col.hsv_to_rgb( i*6, 255, 23 ) color.append([r, g, b]) # Neopixelを同心円上に色を変化させて表示するための計算です r_max = math.sqrt(4.5 * 4.5 * 2) for i in range(NUM_LEDS): x = float(i % 10) - 4.5 # 左から何列目かを計算し、4.5を引いています y = float(i) / 10.0 - 4.5 # 下から何段目かを計算し、4.5を引いています # 中心(4.5, 4.5)からの距離を求め、最大値を1に規格化しています r = math.sqrt(x*x + y*y) / r_max # 同心円状に色を変えて表示するための処理です。 # 色相を中心と端で240°(=6°×40)異なるようにしています。 index.append(int(r*40)) try: # 無限ループです while True: #それぞれのLEDの色を計算し、表示しています for i in range(NUM_LEDS): # 1ステップ前から色を1つずらします index[i] = (index[i] - 1) % 60 # テーブルcolorにより色を取得しています red[i] = color[index[i]][0] green[i] = color[index[i]][1] blue[i] = color[index[i]][2] # 色を表示しています ar_color(i, red[i], green[i], blue[i]) sm.put(ar,8) utime.sleep_ms(10) # ctl-C が押されたときの処理です except KeyboardInterrupt: #### clear ws2812b clear_all()
実際に色を光らせた様子を示します。
以前の独自の色を作る方法による場合
5.まとめ
HSVの各値を求める関数を用いて、独自の色を作る方法について検証してみました。
その結果、以下のことが分かりました。
- 色相(H)はおおよそ、様々な色を表現できていた。
- 彩度(S)は255と最高の値だった。
- 明度(V)は最大と最低で2倍の値の差があった。
このことも踏まえて、さらに明度を一定にしてNeopixelを虹色に光らせてみました。
独自の手法による場合と比べてみたのですが、それほどの違いは(自分の肉眼では)感じられなかったです。
意外と色の違いにより明暗の差をつけたほうが、アクセントがついて面白みがある場合もあるかも知れないです(負け惜しみ)。
以上です。