この記事はOpenCV Advent Calendar 2025の15日目の記事です。
はじめに
最近、かなり久々(数年ぶり?)にcvuiを使用して、個人的には良い感じの使用感だったので、
改めてアドベントカレンダーに投稿🏃♂️
cvuiは、OpenCVの描画を利用して作成された簡易的なGUIライブラリです。
以下のようなGUIが作れます。
ちなみに、以下は少し発展形ですが、ウィンドウ移動や折りたたみなども対応しています。
「OpenCVのデモは、こーいうのでいいんだよ。こーいうので。」と(一部の)ユーザーからは絶賛されています。
ちなみに、ソースコードを見ると分かるのですが、内部ではOpenCVのrectangle()やline()、putText()を駆使してGUIが実現されており(一部の)ユーザーからは
親しみを込めて狂気のライブラリと呼ばれています。
cvuiとは
以下のリポジトリで公開されています。
公式からの引用の機械翻訳ですが、特徴は以下の通りです。
- 軽量で使いやすいユーザー インターフェイス
- 外部依存関係のないヘッダーのみ(OpenCV を除く)
- OpenCV 描画プリミティブのみに基づいています (OpenGL や Qt は不要)
- フレンドリーで C ライクな API (クラス/オブジェクトなどはなし)
- コンポーネントの位置を気にせず簡単にレンダリング可能 (行/列を使用する場合)
- シンプル (かつ強力) なマウス API
- UI コンポーネントの数は適度 (合計 11 個)
- C++ および Python で利用可能 (純粋な実装、バインディングなし)
ちなみに、2022/3/18のコミットを最後に3年以上メンテナンスは止まっています。
個人的には、良い感じに枯れて安定した状態だと思っているので、
メンテナンス止まっていても、簡易な用途に利用するには全然アリかな。と。
使い方
cvuiはヘッダーのみのライブラリです。
C++版はヘッダーオンリー(cvui.h)。Python版も単一ファイル(cvui.py)をプロジェクトに同梱するだけで使えます。
※厳密にいうと、最初に載せた移動ウィンドウのデモはcvuiを応用したUIで、使用するには追加でEnhancedWindow.h/EnhancedWindow.pyが必要です
たとえば、Webカメラの画像にブラーをかけて、トラックバーでカーネルサイズを調整するようなUIは以下のようなコードで書けます。
実装例
import numpy as np
import cv2
import cvui
WINDOW_NAME = "Blur Demo"
def main():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("Error: Cannot open camera")
return
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
cap_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
ui_width = 200
padding = 10
frame_width = padding + cap_width + padding + ui_width
frame_height = cap_height + padding * 2
video_x = padding
video_y = padding
control_x = padding + cap_width + padding
# cvui初期化: ウィンドウ名と waitKey の遅延時間(ms)を指定
cvui.init(WINDOW_NAME, 1)
# trackbar等で値を変更するにはリスト形式で渡す必要がある
kernel_size = [5]
while True:
ret, camera_frame = cap.read()
if not ret:
break
k = max(1, int(kernel_size[0]))
if k % 2 == 0:
k += 1
blurred = cv2.blur(camera_frame, (k, k))
frame = np.zeros((frame_height, frame_width, 3), dtype=np.uint8)
frame[:, :, :] = (49, 52, 49)
frame[video_y:video_y + cap_height, video_x:video_x + cap_width] = blurred
# window: タイトル付きのウィンドウ枠を描画
cvui.window(frame, control_x, 10, 190, 130, "Settings")
# text: テキストを描画
cvui.text(frame, control_x + 10, 40, "Kernel Size:")
# trackbar: スライダーを描画、kernel_size[0]の値が変更される
cvui.trackbar(frame, control_x + 10, 55, 170, kernel_size, 1, 51)
cvui.text(frame, control_x + 10, 100, f"Current: {k}")
# button: ボタンを描画、クリックされるとTrueを返す
if cvui.button(frame, control_x + 10, 150, 170, 30, "Exit"):
break
# imshow: cvui.update() + cv2.imshow() を実行
cvui.imshow(WINDOW_NAME, frame)
# lastKeyPressed: 最後に押されたキーを取得(ESC=27)
if cvui.lastKeyPressed() == 27:
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
ソースコードを見ていただくと分かるかもしれませんが、あまり巨大なUIを書くことには向いていません。
OpenCV標準のGUIよりは、ちょっとリッチにしたいけど、
他のしっかりしたGUIライブラリに頼るほどじゃない。という塩梅の用途向けというところでしょうか。
サポートされているUI
以下のUIがサポートされています。
cvui/example にC++/Pythonの実装例が格納されています。
以下はその実行例です(レナ画像などが必要なものがあり、それらは必要に応じて多少修正しています)
ボタン
例:hello-world.py
画像
例:on-image.py ※画像読み込みをWebカメラ読み込みに改造して実行
チェックボックス
例:canny.py ※画像読み込みをWebカメラ読み込みに改造して実行
テキスト、フォーマット付きテキスト
例:main-app.py
カウンタ
例:main-app.py
トラックバー
on-image.py や canny.py などで表示されているUIのため省略
ウィンドウ
main-app.pyなどで表示されているUIのため省略
長方形
例:mouse.py
スパークライン
例:sparkline.py ※csv読み込みをランダム値に改造して実行
iarea(インタラクションエリア)
例:interaction-area.py

マウスカーソルの領域内、領域外、領域内での押下、クリックを検知
行と列
例:python row-column.py
以下のように、行と列の位置を指定し中にコンポーネントを入れると、中のコンポーネントは位置指定を実施しなくてよい。
実装例抜粋
# In a row, all added elements are
# horizontally placed, one next the other (from left to right)
#
# Within the cvui.beginRow() and cvui.endRow(),
# all elements will be automatically positioned by cvui.
#
# Notice that all component calls within the begin/end block
# DO NOT have (x,y) coordinates.
#
# Let's create a row at position (10,20) with width 100 and height 50.
cvui.beginRow(frame, 10, 20, 100, 50)
cvui.text('This is ')
cvui.printf('a row')
cvui.checkbox('checkbox', checked)
cvui.window(80, 80, 'window')
cvui.rect(50, 50, 0x00ff00, 0xff0000)
cvui.sparkline(values, 50, 50)
cvui.counter(value)
cvui.button(100, 30, 'Fixed')
cvui.image(img)
cvui.button(img, imgGray, imgRed)
cvui.endRow()
# Here is another row, this time with a padding of 50px among components.
padding = 50
cvui.beginRow(frame, 10, 150, 100, 50, padding)
cvui.text('This is ')
cvui.printf('another row')
cvui.checkbox('checkbox', checked2)
cvui.window(80, 80, 'window')
cvui.button(100, 30, 'Fixed')
cvui.printf('with 50px padding.')
cvui.endRow()
# Another row mixing several components
cvui.beginRow(frame, 10, 250, 100, 50)
cvui.text('This is ')
cvui.printf('another row with a trackbar ')
#cvui.trackbar(150, &value2, 0., 5.)
cvui.printf(' and a button ')
cvui.button(100, 30, 'button')
cvui.endRow()
# In a column, all added elements are vertically placed,
# one below the other, from top to bottom. Let's create
# a column at (50, 300) with width 100 and height 200.
cvui.beginColumn(frame, 50, 330, 100, 200)
cvui.text('Column 1 (no padding)')
cvui.button('button1')
cvui.button('button2')
cvui.text('End of column 1')
cvui.endColumn()
# Here is another column, using a padding value of 10,
# which will add an space of 10px between each component.
padding = 10
cvui.beginColumn(frame, 300, 330, 100, 200, padding)
cvui.text('Column 2 (padding = 10)')
cvui.button('button1')
cvui.button('button2')
#cvui.trackbar(150, &value3, 0., 5., 1, '%3.2Lf', cvui.TRACKBAR_DISCRETE, 0.25)
cvui.text('End of column 2')
cvui.endColumn()
# You can also add an arbitrary amount of space between
# components by calling cvui.space().
#
# cvui.space() is aware of context, so if it is used
# within a beginColumn()/endColumn() block, the space will
# be vertical. If it is used within a beginRow()/endRow()
# block, space will be horizontal.
cvui.beginColumn(frame, 550, 330, 100, 200)
cvui.text('Column 3 (use space)')
# Add 5 pixels of (vertical) space.
cvui.space(5)
cvui.button('button1 5px below')
# Add 50 pixels of (vertical) space.
cvui.space(50)
cvui.text('Text 50px below')
# Add 20 pixels of (vertical) space.
cvui.space(20)
cvui.button('Button 20px below')
# Add 40 pixels of (vertical) space.
cvui.space(40)
cvui.text('End of column 2 (40px below)')
cvui.endColumn()
移動可能なウィンドウ
例:ui-enhanced-window-component.py
※cvuiに加えて、EnhancedWindow.h/EnhancedWindow.py が必要
まとめ
cvuiは、OpenCVの描画機能だけを使ってGUIを実現するという若干尖ったライブラリです。
内部実装は rectangle() や putText() の積み重ねという力技ですが、その割り切りのおかげで、(OpenCVが動いているのであれば)環境依存のトラブルもありません。
本格的なGUIフレームワークを導入するほどではないものの、
デモ用にちょっとした操作UIを付けたいといった場面では、今でも十分に実用的だと思います。
メンテナンスは長らく止まっていますが、その分APIは安定しているとも言えます。
※ライブラリはワンファイルなので、もし何かあってもすぐ直せると言えば直せると思いますが
「OpenCVのデモは、こーいうのでいいんだよ。こーいうので。」と思う方は触ってみるのも良いかもしれません🦔











