Yolov5による物体検出 (2): 環境構築と訓練済みモデルを使った動作確認

Yolov5による物体検出 記事一覧

  • (1): 概要
  • (2): 環境構築と訓練済みモデルを使った動作確認 ← イマココ
  • (3): 少量のデータによる訓練と動作確認 ← 後で書く

環境を構築したらまずは訓練済みのデータで動作を確認します。いきなり自前のデータで訓練すると、うまく動かなかったときの原因究明が面倒だからです。それができたら自前のデータで訓練するわけですが、まずは少量のデータ(ある程度は検出可能になるくらい)で訓練してみます。今回はCOCOというパブリックなデータをダウンロードして、その一部を使います。ラベルデータもすでにあるわけですが、ラベルファイルや訓練データのディレクトリ構造など、COCOとYolov5ではいくつか異なるところがあるため、Yolov5が想定している構成になるよう変換するスクリプトを用意してやる必要があります。そのスクリプトに間違いがあると当然うまく動かないので、「スクリプト作成」→「訓練」→「結果確認」→「スクリプト修正」...というループを何度か回すことになります。最初から大量のデータを入れると1ループに時間がかかるので、まずは少量のデータでやる方が良いです。

動作環境

以下の2つで動作を確認しています。

  • Ubuntu-20.04 (WSL2)
    • CPUのみ
    • Python-3.9.4をpyenvで入れる。
    • 必要なパッケージはpipで入れる。virtualenvも使用。
  • Google Colaboratory (ハードウェアアクセラレータ: GPU)
    • Python-3.7.10 (現在のColaboratoryのデフォルト環境)
    • 必要なパッケージはpipで入れる

環境構築 - Ubuntu

Pythonはpyenvでインストールしたものを使います。バージョンは手元に入っていた3.9.4。Pythonのバージョンについてはそれほど強いバージョン制約はなさそう。Colaboratoryの方では3.7.10で動いているのでその辺りのバージョンなら多分動くでしょう。

パッケージはpipで入れますが、virtualenvも使うことにします。ディープラーニングでよく使われるtensorflow, pytorchはバージョンの制約がそれなりあるので環境を分けておいた方が無難です。

Pythonインストール

# Python本体インストール
$ sudo apt install liblzma-dev
$ pyenv install 3.9.4

注意点としてはliblzma-devパッケージをPython本体ビルド前にインストールしておきます。lzmaというモジュールがPython本体に同梱されていますが、liblzma-devパッケージが入っていないとそれが有効化されずエラーになってしまいます。手元にすでにビルド済みのPythonがあり、かつまだliblzma-devをインストールしていないなら、そのPythonは多分使えません。一度アンインストールして再ビルドしましょう。

Yolov5ダウンロード

ディレクトリ構成は以下のような感じにします。

$HOME/yolov5/
        +- dataset/ # 学習用のデータセットを置く(後で作る)
        +- yolov5/  # Yolov5スクリプト

virtualenvの設定とパッケージインストール

$ $HOME/yolov5
$ pyenv virtualenv 3.9.4 yolov5
$ echo "yolov5" > .python-version
$ cd yolov5
$ pip install -r requirements.txt

環境構築 - Colaboratory編

あとで書く。

訓練済みモデルによる検出

まずは訓練済みのモデルを使って動くかどうか確かめてみます。単にpython detect.pyを実行すれば自動的にモデルのダウンロードを行うのですが、そういうのにあまり頼るとブラックボックスになってしまうので、ここでは手動でダウンロードします。モデルはgithubのリリースページからダウンロードできるので、とりあえずyolov5s.ptをダウンロードします。置き場所はどこでもよいですが、yolov5の直下に置きました。

あとは検出を試す画像ですが、data/images以下にbus.jpgzidane.jpgが含まれているのでこれを使います。

モデルと画像が準備できたら、以下のコマンドで実行します。

$ python detect.py --weights yolov5s.pt --source data/images --device cpu

検出結果はruns/detect/exp以下にできるので、確認してみましょう。なお、以下の例外が出た場合はlzmaモジュールができていないので、上の説明を参考にしてPythonをビルドし直して再度試してください。

    from _lzma import *
ModuleNotFoundError: No module named '_lzma'

train.pyのオプション

train.pyのオプションのうちよく使いそうなものを以下に挙げます。

オプション 意味
--weights PATH モデル (ptファイル)
--source PATH 検出する画像を含むディレクト
--conf-thres THRESH 検出する閾値。デフォルトは0.25
--device DEVICE CUDAデバイス0, 0,1,2,3など。CPUの場合はcpu
--save-txt 検出結果のbboxなどをテキストファイルとして保存する
--save-conf 上記テキストファイルに検出結果のconfidenceも出力する
--project PROJECT 出力結果の保存先。PROJECT/NAMEに保存される
--name NAME 出力結果の保存先。PROJECT/NAMEに保存される

次回以降に続く。

Yolov5による物体検出 (1): 概要

Yolov5による物体検出 記事一覧

最近ちょっと物体検出を試す機会があったので、少しまとめておきます。

物体検出の方法にはいくつかの方式がありますが、今回はYolov5というの使います。選んだ理由は単に以前に受講したCourseraのDeep Learning講座でYoloを習ったから、というだけなので深い意味はないです。v5はオリジナルのYolo作者とは別の人が作ったものだからYoloを名乗るのはおかしい、という指摘もあるみたいですが、まあ細かいことは気にしない。

作業は以下の手順で進めます。

  1. 環境構築
    • Pythonとパッケージのインストール
    • 訓練済みモデルを使った動作確認
  2. 少量のデータによる訓練と動作確認
    • 訓練データフォーマットの調査
    • 訓練データ生成スクリプトの作成
  3. 本番データでの訓練と結果確認

環境構築

まずは必要なパッケージをインストールして動作を確認します。Yolov5をはじめ、たいていのツールには訓練済みモデルが用意されているのでまずはこれを使います。いきなり自前のデータで訓練すると、うまく動かなかったときの原因究明が面倒だからです。必要なパッケージはたいていドキュメントに書いてあるので簡単、と思いきや以下のような理由で意外と苦労することが多いです。

  • Python本体とフレームワーク(tensorflow, pytorchなど)のバージョンの相性
  • CUDAが使えるかどうか
  • LinuxWindows
  • パッケージをpipで入れるか、Anacondaか

訓練データフォーマットの調査

とりあえずの動作確認ができたら次にやるのは訓練データの構成とフォーマットの調査です。画像からの物体検出の学習には画像とラベルの情報が必要ですが、その構成やフォーマットはツールによってさまざまです。以下のようなことが明確になっていなければ訓練データを作ることができません。

  • 画像・ラベルファイルを置くときのフォルダ構成
  • ラベルファイルは全画像の情報を1つのファイルに入れるか、1画像に対して1ファイルか
  • ラベルファイルのフォーマットの詳細(XMLJSONCSVなど)
  • バウンディングボックスはx, y, width, heightか、対頂点の座標(x1, y1, x2, y2)か
  • バウンディングボックスの座標等の値はピクセルか、幅・高さを1としたときの相対値か

訓練データ生成スクリプトの作成

これらが明らかになったら、学習させたいデータセットからツールが期待する構成・フォーマットのデータセットを生成するスクリプトを書きます。このとき、使用するデータの数を指定できるようにしておき、最初は極少数(例えば10個程度)にし、epochも少なくします。もちろんこれではまともなパフォーマンスは望めませんが、まずは訓練スクリプトがエラーなく実行され、モデルが出力されることを確認します。また、このときかかる時間も計測しておきます。

訓練スクリプトが無事動いたらもう少し数を増やして(例えば1000個 x 30 epochs)試してみます。これくらいやればある程度は認識できていることが確認できるかと思います。これで全くデタラメな結果が出たら、多分何かが間違ってます。

本番データでの訓練と結果確認

ここまでできたらもっと多くのデータを使って訓練します。時間はかかるでしょうが待つだけです。

ということで次回「環境構築と訓練済みモデルを使った動作確認」に続く。

opencvr: OpenCV Rubyバインディング

OpenCVの新しいRubyバインディングであるopencvr-0.1をリリースしました。とは言っても現状はまだ足りない機能が多く実用的とは言い難い状態なので、どちらかと言うと「OpenCVRubyバインディングの開発を始めました」というアナウンスという位置づけです。

https://github.com/wagavulin/opencvr

gemはまだ作っていないのでコマンド一発でインストールとはいかないですが、apt/brewでインストールしたOpenCVを使ってビルドできるようにはなっています。Ubuntu-20.04かmacOSの環境なら試すのはそんなに難しくないはずなので、気が向いたら試してもらえればと思います。方法はREADME.mdのHow to installを参照してください。

サンプルとスクリーンショット

元画像

f:id:wagavulin:20150315101002j:plain

Drawing

画像に矩形や直線などの図形や文字を書き込みます。

f:id:wagavulin:20210707190930j:plain

#!/usr/bin/env ruby
require 'cv2'
img = CV2::imread("input.jpg")
CV2::putText(img, "Hello OpenCV", [50, 50], CV2::FONT_HERSHEY_DUPLEX, 1.0, [0, 0, 0], lineType: CV2::LINE_AA)
CV2::imwrite(__dir__ + "/out-drawing.jpg", img)

油絵風

f:id:wagavulin:20210707190936j:plain

#!/usr/bin/env ruby
require 'cv2'
img = CV2::imread("input.jpg")
out = CV2::Xphoto.oilPainting(img, 2, 5, CV2::COLOR_BGR2Lab)
CV2.imwrite(__dir__ + "/out-oil.jpg", out)

鉛筆画風

白黒

f:id:wagavulin:20210707190946j:plain

色付き

f:id:wagavulin:20210707190954j:plain

#!/usr/bin/env ruby
require 'cv2'
img = CV2::imread("input.jpg")
out1, out2 = CV2.pencilSketch(img, sigma_s: 60, sigma_r: 0.07, shade_factor: 0.05)
CV2.imwrite(__dir__ + "/out-pencil1.jpg", out1)
CV2.imwrite(__dir__ + "/out-pencil2.jpg", out2)

使い方

Python版とだいたい同じです。

// C++
cv::Mat img = cv::imread("input.jpg", cv::IMREAD_COLOR);
# Python
img = cv2.imead("input.jpg", cv2.IMREAD_COLOR)
# Ruby
img = CV2::imread("input.jpg", CV2::IMREAD_COLOR)
  • C++ APIでオプショナルになっている引数はRubyでも省略可能です。
  • オプショナルな引数はキーワード引数も使えます。キーワード名はC++ APIの仮引数名と同じです。
  • 必須引数はキーワード引数にはできません。
  • CV2以外の名前空間名は、Rubyでは最初の文字のみ大文字にしたものになっています。
    • C++cv.xphoto.oilPainting()CV2::Xphoto::oilPainting()です。
  • cv::Size, cv::Point, cv::RectなどはArrayになります。
    • 例えばcv::Sizeは数値2つを持つArrayです。
  • 引数が出力に使われる場合(引数が非const参照など)、結果は戻り値として帰ります。

OpenCVAPI ReferenceにはPython APIも載っているで詳しくはそちらを参考にしてください。例えば以下のcv::clipLine()は、C++では3つの引数 imgSize, pt1, pt2を受け取りboolを返しますが、見ての通りpt1, pt2は非const参照で出力にも使われます。従ってPython/Ruby APIでは戻り値が3つになります。

f:id:wagavulin:20210708192137p:plain

なぜ作ったか

OpenCVは非常に有名な画像処理・コンピュータビジョンのライブラリで、C++で書かれてます。Pythonバインディングは公式に提供されていますがRubyバインディングについてはgemがいくつかありますが、決定版と言えるものはない感じです。古いバージョンについてはruby-opencvがよく使われていましたが、最新のOpenCV-4系では使えません。OpenCV-2.XまではC++/C両方のAPIが用意されており、ruby-opencvはC APIを使ってバインドされていましたが、4.0からはC APIが廃止されてしまったためです。

red-opencv, opencv-glib

新しいOpenCVに対応したRubyバインディングとして、red-opencvopencv-glibがあります(2つを組み合わせて使います)。まずopencv-glibはOpenCVC++ APIのGObjectバインディングを提供します。GObjectの詳細はここでは省きますが、GObjectからはRubyだけでなくPerl, Javaなどといった様々なプログラミング言語バインディングを自動生成することができます。これを使ってRubyバインディングを提供するのがred-opencvです。

このopencv-glibについては私も以前に開発に参加して私のコードも少し入っていますが、手が止まってしまいました。理由は大きく2つあります。

前者の問題についてはglibに対する私の知識不足もあるので、実はたいした問題ではないかもしれません。しかし後者は由々しき問題です。試しにPython APIでバインドされている関数・enumをざっと数えたところ4000近くありました。

  • クラスに属さないグローバルな関数: 892
  • クラスに属する関数: 2925
  • enum型: 195

手作業でやると毎日休まず1日10個作っても10年以上かかるわけで、この数字を見たときに心が折れました。

ちなみに別の方法としてPyCallを使うというのがあります。私は試してはいませんが、Pythonバインディングが既に存在する以上、PyCallを使えばRubyからも呼べるはずです。ただそれを言ってしまうと話が終わってしまうのでそこはスルーして進めます。とにかくRubyから直接呼びたいのです(あんまり深い理由はないですが)。

Pythonバインディングとopencvr

では公式でサポートされているPythonバインディングはどうしているかというと、バインディングコードを自動生成しています。OpenCVC++ APIのヘッダファイルからインターフェース情報を読み取り、それを基にバインディングコードを生成します。具体的にはhdr_parser.pyというスクリプトでヘッダファイルの情報を読み取り、それを基にgen2.pyPythonバインディングコードを生成します(もっと細かく言うと、自動生成コード以外ににいくつかの.cpp, .hppを使っています)。

f:id:wagavulin:20210708022558p:plain

この仕組みを真似すればRubyバインディングも自動生成できるはず、という発想で作ったのがopencvrです。hdr_parser.pyはそのまま流用し、gen2.pyを独自のものに置き換えています。Python用のgen2.pyやその他.cppファイルなど合わせて4000行くらいあり、それのRuby版を作るわけですが、4000個の関数のバインディングコードを書くよりはかなり作業量は減るはずです。

実装状況

こうして始めたopencvrですが、実装状況はまだ未熟です。ざっくり言うとバインドできているのは以下の条件を満たすもののみです。

  • クラスに属さないグローバル関数である
  • 引数・戻り値ともint, floatなどの基本型もしくはcv::Size, cv::Rectなど、Ruby側でArrayにバインドされているものである。
    • std::vector<int>など、これらを要素に持つstd::vectorはサポート。
  • クラスは現状未サポートだが、cv::Matのみは使用可能(というかこれがないと始まらない)。ただし使えるメソッドはcols(), rows(), channels(), at()のみ。

グローバル関数のみ、と聞くとほとんど何もできないじゃないかと思われそうですが、OpenCVAPIはグローバル関数になっているものが結構あるのでそれなりに使えます。詳しいバインド状況はWikiページにあります。

またOpenCVの中心的なクラスであるcv::Matのメソッドがほとんど対応していないのは理由があって、次に書きます。

cv::Matと数値計算ライブラリ

OpenCVでは画像データなど多くのデータを表すのにcv::Matクラスを使います(APIリファレンス上ではcv::InputArray, cv::OutputArray, cv::InputOutputArrayになっているところ)。Python APIではこのcv::Matクラスに対応するPythonのクラスを作るのではなく、Numpyのndarrayクラスを使っています。例えば画像ファイルを読み込むcv::imread()関数は、C++ APIではcv::Matインスタンスを返しますが、Python APIではndarrayを返します。Pythonの画像処理・機械学習・データサイエンス系のライブラリの多くはndarrayを使っているので、それらのライブラリと容易に連携させることができるわけです。

import cv2
img = cv2.imread("input.jpg")
print(img.__class__) # => <class 'numpy.ndarray'>

これをどうやって実現しているかというと、cv::MatのAllocatorという機能を使っています。これはcv::Matが内部で使うメモリを確保するときに使用する関数を指定するもので、これとNumpyのC APIを組み合わせて実現します。例えばcv::imread()を読んだとき、以下のことをやります。

  • cv::imread()の戻り値 (m) とは別に空のcv::Matインスタンス (temp) を作る。
  • tempのAllocatorに独自のAllocatorをセットする。
  • mtempにコピーする。
    • このとき独自のAllocatorが使われる。その中ではNumpyのPyArray_SimpleNew()を使ってメモリを確保する。
    • 確保した領域へのポインタがcv::Matインスタンス内にセットされる。

出来上がったtempPython側に返すポインタは以下のようになっています。

f:id:wagavulin:20210707192520p:plain

現状のopencvrはこのようなことはやってはおらず、Ruby側にもMatクラスを定義するようになっています。Pythonと同様のことをするならNumo::NArrayが候補になると思いますが、そもそも技術的に可能かどうかの検討もしていないので今後どうするかは未定です。cv::Matクラスのバインディングが手抜きなのはこれが理由です。

なお検討していない理由は時間の都合もさることながら、Numo::NArrayのC APIの使い方がさっぱり分からないというのが原因なので、分かりやすいドキュメントなどあったら教えてほしいです。

まとめ

  • OpenCVRubyバインディング作ってます
  • 現状は未対応の部分がたくさんあります
  • Python版の仕組みを真似しているので、Python版の同等のことが現実的な工数できるはずです
  • Numpyみたいなものを使うかは未定です

と、ここまで書いたところで改めてgemを探すとropencvというgemがC++ヘッダから自動的にバインドするRubyインターフェースを提供しているっぽいのを見つけました。opencvr-0.1.0を作るまでにそれなりに時間使ったのですが、無駄だったんですかねぇ。

Ubuntu 18.04 GNOME設定

以前にUbuntu 16.04のUnityの設定についての記事を書いたが、今回はUbuntu-18.04の話。

Ubuntuを最初に触ったのは8.04の頃だったが、その頃のデスクトップ環境のデフォルトはGnome2だった。Gnome2のデフォルト状態も色々不満があったが、たいていのことは設定変更で解決できた。特にGConfという、Windowsのレジストリみたいなものがあり、それを使うとかなり細かいこと(例えばタスクバーが表示されるときのアニメーションの速度など)まで設定できたので重宝した。

それが11.04になるとUnityという独自のデスクトップ環境になったが、もはや過去の遺物なので詳細は省くが、これがまた使いにくい代物だった。「Unityの開発者も新たなデスクトップ環境を開発するにあたっては使いやすさを当然考えているはず。それが受け入れられないのは自分が古いものに囚われているからかもしれない」と考えて何度も慣れようとしたが結局挫折した。幸いUnityが使いにくいと思っていたのは自分だけではなかったようで、Unity Tweak Toolといったツールも現れたことでようやく状況は改善され、これを使ってUnity環境を改善したのが前回の記事だ。

その後CanonicalはUnityの開発を中止し、17.04からデフォルトのデスクトップ環境がGnomeに戻った。しかしそれはGnome2ではなくGnome3である。個人的な感想としてはGnome3はUnityと同じくらい使いにくい。それを何とかしようというのが今回の目標である。

基礎知識と準備

Gnome3の設定・改良は次の3種類を使う。

設定
Gnome3に最初から入っている「設定」
Gnome Tweak Tool
設定からは変更できない細かい設定を行うもの。デフォルトでは入っていないので`sudo apt install gnome-tweak-tool`でインストールしておく。
Gnome Shell拡張
Gnome Shellは設定変更だけでなく、ユーザが機能を拡張することができるようになっている。それらの多くはhttps://extensions.gnome.org に置かれており、ここから使いたい拡張機能をダウンロードしてインストーする。なお、拡張機能のインストールには事前に準備が必要なので後述する。

Gnome Shell拡張のための準備

Gnoem Shell拡張を使うにはchrome-gnome-shellとブラウザの拡張が必要だ。chrome-gnome-shellはsudo apt install chrome-gnome-shellでインストールできる。なお名前に"chrome"が入っているがGoogle Chromeとは関係ない。

またFirefoxで何らかの拡張機能のページ、例えばhttps://extensions.gnome.org/extension/15/alternatetab/ に行くと、ブラウザ拡張が入っていない場合は以下のようなメッセージが出ているだろう。このメッセージの中の"Click here to install browser extension"をクリックすればインストールできる。

f:id:wagavulin:20190428192319p:plain

ブラウザ拡張がインストールされていれば以下のようにOn/Offのトグルボタンになっているだろう。これをOnにすればインストールできる。

f:id:wagavulin:20190429091112p:plain

これで準備ができたので実際に設定を変更していく。

Alt-tabをウィンドウ単位にする

Unityもそうだったが、Gnome ShellでもAlt-tabの動作は「ウィンドウの切り替え」ではなく「アプリのの切り替え」だ。なので、同じアプリのウィンドウが複数ある場合は最初にアプリを選択し、その後にウィンドウを選択するという2段階の動作が必要である。下の例ではNautilus(標準のファイルマネージャ)のウィンドウが2つあるため、2段階目で選択している。

f:id:wagavulin:20190429092550g:plain

これをウィンドウ単位にするにはGnome Shell拡張のAlternateTabを使えば良い。インストールは上述のとおり、トグルボタンをOnにすれば良い。これでAlt-tabの動作が以下のようになる。

f:id:wagavulin:20190429092735g:plain

なお、以下のようなメッセージが出た場合はchrome-gnome-shellがインストールされていないのでインストールする。

f:id:wagavulin:20190429091601p:plain

アプリケーションメニュー

Windowsのスタートメニューに相当するものがGnome Shellにはなく不便なので追加する。これもGnome Shell拡張のApplicaitons Menuで追加できる。インストールすると左上が以下のように「アプリケーション」に変わる。元々は「アクティビティ」だったはずだ。

f:id:wagavulin:20190429093421p:plain

が、デフォルト設定のままではこれを押しても何も起きないという問題がある。Gnome Tweak Toolから「トップバー」を選び、そこにある「Activities Overview Hot Corner」を「オン」にすれば動くようになる。

f:id:wagavulin:20190429094524p:plain

うまくいけば以下のようなメニュー表示できるようになるはずだ。

f:id:wagavulin:20190429094618p:plain

ワークスペース(仮想デスクトップ)の設定

ワークスペース数の固定化

Gnome Shellはデフォルトでワークスペース(仮想デスクトップなどとも呼ばれているもの)に対応しているが、ワークスペースは必要に応じて自動的に増減するようになっている。具体的には最初はワークスペースは1つだけだが、何かのウィンドウのタイトルバーを右クリックして「下側のワークスペースへ移動する」を選べば自動的に2つ目のワークスペースができる。

はじめから決まった数だけ作る場合はGnome Tweak Toolの→「ワークスペース」から「静的ワークスペース」を選択する。

f:id:wagavulin:20190429095007p:plain

ショートカットキーの設定

ワークスペースの切り替えはキーボードでできた方が便利なので設定する。「設定」→「デバイス」→「キーボード」からショートカットキーを設定する。自分の場合は「ワークスペース{1,2,3,4}へ切り替える」に対してそれぞれ「Alt+1」「Alt+2」「Alt+3」「Alt+4」を設定している。ここら辺は好みと、自分がよく使うアプリと衝突しないか、というあたりとの兼ね合いなのでよく考えて決めてほしい。

f:id:wagavulin:20190429095314p:plain

アニメーションの無効化

これでキーボードでの切り替えができるようになったが、実際に使ってみると切り替え時の表示が目障りに感じる。

f:id:wagavulin:20190429095937g:plain

まず画面全体がスライドするアニメーションが目障りだ(キャプチャー時のフレームレートが低いためこの動画だと分かりにくいかもしれないが)。もう一つ、画面中央に表示されるインジケータも大きくて目立つため目がチカチカする。この2つを何とかしよう。

まず画面全体のスライドアニメーションだが、これはGnome Tweak Toolの「外観」→「アニメーション」をオフにすれば無効化できる。ただし他のアニメーション(例えばウィンドウを最大化・最小化したときなど)も無効化されるので好みによるところかもしれない。

インジケータの無効化は拡張機能で行う。Disable Workspace Switcher Popupを入れれば無効化できる。

f:id:wagavulin:20190429100426g:plain

ワークスペース番号の表示

インジケータを無効化したことで今度は今いるワークスペースがどこかを知るのが面倒になったので、ワークスペース番号をツールバーに表示する Workspace Indicatorを入れる。

f:id:wagavulin:20190429110444p:plain

これでワークスペースが快適に使えるようになった。

テーマの設定

UnityからGnome3に変わっても相変わらず見た目はオレンジと紫を基調にしたやつのままだ。Ubuntu開発者にとってはこれが良いのかもしれないが、個人的にはあまり好きではないので変更する。

もちろん壁紙の部分の変えるのは簡単で、デスクトップを右クリックして「背景を変更する」を選べば良い。今回変えたいのはウィンドウ右上のボタンやトグルボタンといったUI部品の色だ。

f:id:wagavulin:20190429135442p:plain

これらを変えるにはテーマを設定する。Gnome Tweak Toolの「外観」→「テーマ」から変更できる(なぜこの程度のことが標準の「設定」からできないのか)。「アプリケーション」「アイコン」「カーソル」「Gnome Shell」の4つがあり、それぞれ変更箇所が異なる。「アプリケーション」と「Gnome Shell」の区分ははっきりとは知らないが、試したところトップバーとサイドバーはGnome Shellの範囲、UI部品の形や色などは「アプリケーション」になるようだ。

f:id:wagavulin:20190429135545p:plain

f:id:wagavulin:20190429135702p:plain

Gnome Shellテーマ変更の有効化

上の図を見て分かるとおり、Gnome Shellのテーマ変更はこのままではできない。Gnome Shell拡張のUser Themesを入れればこの部分が使えるようになる。あらかじめ入れておこう。

テーマのダウンロードと設置

テーマはgnome-look.orgにたくさんあるのでここから探すのが良いだろう。昔はこの手のサイトにはエロテーマも結構あったので会社からアクセスするのは躊躇したものだが、最近はそうでもないようで随分と健全になったものだ(とは言っても今でも微エロ程度の画像はチラホラ見えるので会社からアクセスするときは一応注意しよう)。

テーマはCursors, Gnome Shell Themesなどいくつかの分類があるが、今回やりたいUI部品についてはGTK3 Themesになるようだ。今見たところ、Flat Remix GTK/Elementary themeというのが評価が高いようなのでまずはこれで試してみよう(特に好みというわけではないが)。下の方にある「Files」を選択してダウンロードする。

f:id:wagavulin:20190429135803p:plain

テーマの置き場はいくつかあり、全ユーザ共通にするなら/usr/share/themes以下で、すでにAdwaitaやAmbianceといったインストール済みのテーマがあるだろう。自分用であれば$HOME/.themes以下でもよい。いちいちsudoしなくても良いためこちらの方が便利かもしれない。$HOME/.themesはデフォルトでは作られていないので自分で作り、そこでダウンロードしたアーカイブを解凍する。アーカイブは.tar.gzだったり.zipだったりするが、.tar.gz, .tar.bz2, .tar.xzあたりならtar xvf xxx.tar.gz解凍できる。.zipならunzip xxx.zipだ。

ただし時折解凍後にフォルダを作らずその場所にファイルをばら撒くようになっているものもあるので(最近はあまり見なくなったが)、解凍後にフォルダ・ファイル一覧を確認してからの方が良いかもしれない。tarのときはtar tvf xxx.tar.gzunzipならunzip -t xxx.zipで実際に解凍せずにファイル一覧を見ることができる。今ダウンロードしたFlat-Remix-GTK-Blue_2.16.tar.xzは問題ないようだ。

これで$HOME/.themes/Flat-Remix-GTK-BlueフォルダができればGnome Tweak Toolからテーマを選択できるようになる。Gnome Tweak Toolが既に起動済みであれば一度終了してから再度開く。テーマを選択すれば以下のようになるだろう。

f:id:wagavulin:20190429135842p:plain

Vertexテーマのインストール

いくつかテーマをインストールして試してみたが、今のところVertexを使っている。ただしこのテーマはすぐに使えるアーカイブはなく、ソースコードを自分でビルドする必要がある。ビルド手順はテーマのGithubページに書かれており、特別難しいことがあるわけではないが、Linuxの開発環境にある程度馴染みがないと大変かもしれない。面倒なので今回はそこまで説明しないが、結果は以下のような感じである。

f:id:wagavulin:20190429140037p:plain

テーマ選定について

テーマを選ぶときはデフォルトとあまり大きく変わるものでない方が良いかもしれない。最近はナイトモードやダークモードといった名前で黒基調にする機能がWindowsやmacOSにも見られ、実際個人的には黒基調の方が好きだが、GTK3テーマを黒基調にするとアプリのよっては外観が損なわれることがある。例えば以下はVertexに含まれているVertex-Darkにしたもので、この画面だけでは特に問題ないが、これを適用するとWebページの表示も大きく影響を受ける。

f:id:wagavulin:20190429140132p:plain

以下は<textarea>要素を持つウェブページを表示したところだ。CSSの類は一切設定していないので通常は白背景で黒文字になるが、Vertex-Darkを適用するとこのように黒背景白文字になる。こんな感じでウェブページの外観にも影響を与えるので場合によっては見た目が変な感じになることがある。

f:id:wagavulin:20190429140236p:plain

その他雑多な設定

面倒なのでスクリーンショットは貼らないが、今のところ以下の設定をしている。

  • ツールバーに日付を表示する
    • デフォルトでは「月曜日 01:23」のような感じだが、ここに日付を追加できる。
    • Gnome Tweak Tool -> 「トップバー」 -> 「日付」で設定できる。
  • ロックとスクリーンセーバー
    • VMwareの仮想マシンとして使っているので画面ロック・スクリーンセーバーは(必要あれば)ホストであるWindows側でやるためUbuntu側には必要ない。これらを無効にする。
    • 「設定」→「プライバシー」→「画面ロック」→「画面オフ後にロックするまでの時間」で時間を設定できる。
    • また「電源」→「ブランクスクリーン」を「しない」にする。