ファイルから画像を読み込む方法。それから矩形で画像を切り出す方法。表示はwxWidgetsに出力している。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #pragma comment(lib,"skia.dll.lib") #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkBitmap.h" #include "skia/include/core/SkImage.h" // 画像読み込み #include "skia/include/core/SkStream.h" #include "skia/include/codec/SkCodec.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string> void getPixelRGB(std::vector<std::uint8_t>& rgb,const SkBitmap& bitmap) { if (!bitmap.readyToDraw()) { return; } if (bitmap.colorType() != kRGBA_8888_SkColorType && bitmap.colorType() != kBGRA_8888_SkColorType) { return; // Unsupported format } int Width = bitmap.width(); int Height = bitmap.height(); size_t rowBytes = bitmap.rowBytes(); const auto& info = bitmap.info(); int channels = info.bytesPerPixel(); std::uint8_t* pixels = (std::uint8_t*)bitmap.getPixels(); rgb.resize(Width * Height * 3); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { size_t pos_ppm = (i * Width + j) * 3; size_t pos_sk = (i * rowBytes + j * channels); rgb[pos_ppm + 0] = pixels[pos_sk + 2]; rgb[pos_ppm + 1] = pixels[pos_sk + 1]; rgb[pos_ppm + 2] = pixels[pos_sk + 0]; } } } //////////////////////////////////////////////////////////////////
//! @brief 画像読み込み関数 //! @param path 画像ファイルのパス //! @param bitmap 読み込んだ画像を格納するビットマップ //! @return 読み込みに成功したらtrue bool LoadImageToBitmap(const std::string& path, SkBitmap* bitmap) { // ファイルからデータを読み込む
sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str()); if (!data) { return false; // データの読み込みに失敗 } // データからSkCodecを作成 std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(data)); if (!codec) { return false; // コーデックの作成に失敗 } // ロード先のビットマップの情報を設定 SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); // ロード先のメモリ確保 if (!bitmap->tryAllocPixels(info)) { return false; // メモリ確保に失敗 } // ロード先へ画像をデコード SkCodec::Result ret = codec->getPixels( info, // 画像の情報 bitmap->getPixels(), // ロード先のメモリ bitmap->rowBytes() // ロード先の1行のバイト数 ); if (ret != SkCodec::kSuccess) { return false; // 画像のデコードに失敗 } return true; }
//////////////////////////////////////////////////////////////////
      //! @brief 画像の一部を切り出す //! @param dst 切り出した画像を格納するビットマップ //! @param src 元画像 //! @param croprect 切り出す範囲 //! @return なし void CropImage(SkBitmap* dst, const SkBitmap& src, const SkIRect croprect) { sk_sp<SkImage> src_bmp_img = src.asImage();// SkBitmapをSkImageに変換 SkCanvas canv(src); dst->allocN32Pixels(croprect.width(), croprect.height());// 画像のメモリ確保 SkCanvas croppedCanvas(*dst);// 結果のSkBitmapからSkCanvas作成 SkIRect dstRect = SkIRect::MakeWH(croprect.width(), croprect.height());// 結果の書き込み範囲作成 SkSamplingOptions samplingOptions(SkFilterMode::kLinear); croppedCanvas.drawImageRect( src_bmp_img, // 元画像 SkRect::Make(croprect), // 切り取り範囲 SkRect::Make(dstRect), // 描画範囲 samplingOptions, // サンプリングオプション nullptr, SkCanvas::kStrict_SrcRectConstraint // 切り取り範囲を超えた描画を禁止 ); }
// ウィンドウ作成 class MyFrame : public wxFrame { SkBitmap _bmp; public: void PostCreate() {
// 画像をファイルから読み込む LoadImageToBitmap("test.png", &_bmp); Bind(wxEVT_PAINT, &MyFrame::OnPaint, this); this->Layout(); // レイアウトの更新 } MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) { // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行 // コンストラクタはウィンドウ生成イベント扱い CallAfter(&MyFrame::PostCreate); } void OnPaint(wxPaintEvent& event) { // 切り出したい範囲をSkIRectで定義 SkIRect cropRect = SkIRect::MakeXYWH(300, 300, 400, 200); // 切り出した画像を格納する画像 SkBitmap croppedBitmap; // 切り出し処理 CropImage(&croppedBitmap, _bmp, cropRect); ////////////////////////////////////////// SkBitmap* dispbmp = &croppedBitmap; std::vector<std::uint8_t> rgb; getPixelRGB(rgb,*dispbmp); wxImage img(dispbmp->width(), dispbmp->height(), (unsigned char*)rgb.data(), true); wxBitmap wxbitmap(img); wxPaintDC dc(this); dc.DrawBitmap(wxbitmap, 0, 0, true); } private: }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);

SkiaのSkBitmapをwxWidgetsのwxBitmapに変換して表示する。
wxWidgetsがRGBなのに対して、SkiaはRGBをサポートしていないので、32bit→24bitへの変換が必要になる。変換関数らしきものを見つけたがどうしてもうまく動かなかったので手動で変換している。
// https://docs.wxwidgets.org/3.0/overview_helloworld.html // プリプロセッサに以下二つを追加 // __WXMSW__ // WXUSINGDLL // サブシステムをWindowsに設定(WinMainで呼び出すので) // Windows (/SUBSYSTEM:WINDOWS) #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <wx/gdicmn.h> // wxPointに必要 #include <wx/frame.h> // wxFrameに必要 #ifdef _DEBUG #pragma comment(lib,"wxbase32ud.lib") #pragma comment(lib,"wxbase32ud_net.lib") #pragma comment(lib,"wxbase32ud_xml.lib") #pragma comment(lib,"wxmsw32ud_adv.lib") #pragma comment(lib,"wxmsw32ud_aui.lib") #pragma comment(lib,"wxmsw32ud_core.lib") #pragma comment(lib,"wxmsw32ud_gl.lib") #pragma comment(lib,"wxmsw32ud_html.lib") #pragma comment(lib,"wxmsw32ud_media.lib") #pragma comment(lib,"wxmsw32ud_propgrid.lib") #pragma comment(lib,"wxmsw32ud_qa.lib") #pragma comment(lib,"wxmsw32ud_ribbon.lib") #pragma comment(lib,"wxmsw32ud_richtext.lib") #pragma comment(lib,"wxmsw32ud_stc.lib") #pragma comment(lib,"wxmsw32ud_webview.lib") #pragma comment(lib,"wxmsw32ud_xrc.lib") #else #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") #endif #pragma comment(lib,"skia.dll.lib") ///////////////////////////////////// #include "skia/include/core/SkCanvas.h" #include "skia/include/core/SkBitmap.h" ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// #include <string>
// SkBitmapをRGBの配列に変換
void PixelRGB(std::vector<std::uint8_t>& rgb,const SkBitmap& skbmp) { if (!skbmp.readyToDraw()) { return; } if (skbmp.colorType() != kRGBA_8888_SkColorType && skbmp.colorType() != kBGRA_8888_SkColorType) { return; } rgb.clear(); int Width = skbmp.width(); int Height = skbmp.height(); size_t rowBytes = skbmp.rowBytes(); const SkImageInfo& info = skbmp.info(); int channels = info.bytesPerPixel(); std::uint8_t* pixels = (std::uint8_t*)skbmp.getPixels(); rgb.resize(Width * Height * 3); for (int i = 0; i < Height; i++) { for (int j = 0; j < Width; j++) { size_t pos_ppm = (i * Width + j) * 3; size_t pos_sk = (i * rowBytes + j * channels); rgb[pos_ppm + 0] = pixels[pos_sk + 2]; rgb[pos_ppm + 1] = pixels[pos_sk + 1]; rgb[pos_ppm + 2] = pixels[pos_sk + 0]; } } }
// ウィンドウ作成 class MyFrame : public wxFrame { SkBitmap my_bitmap; public: void PostCreate() {
// 画像の作成・描画 my_bitmap.setInfo(SkImageInfo::Make(400, 400, kBGRA_8888_SkColorType, kPremul_SkAlphaType)); my_bitmap.allocPixels(); SkCanvas canvas(my_bitmap); // 背景を白にクリア canvas.clear(SK_ColorWHITE); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint);
      // OnPaintイベントを設定
      Bind(wxEVT_PAINT, &MyFrame::OnPaint, this);
      this->Layout(); // レイアウトの更新
    }
    MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
        : wxFrame(NULL, wxID_ANY, title, pos, size)
    
    {
      // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
      // コンストラクタはウィンドウ生成イベント扱い
      CallAfter(&MyFrame::PostCreate);
    }
    // OnPaintイベント
    void OnPaint(wxPaintEvent& event) {
      // SkBitmapをRGBへ変換 int width = my_bitmap.width(); int height = my_bitmap.height(); std::vector<std::uint8_t> rgb; rgb.resize(width * height * 3); // 変換関数(自作)呼び出し PixelRGB(rgb, my_bitmap); // RGBをwxImageへ変換 wxImage img(width,height, (unsigned char*)rgb.data(), true); wxBitmap wxbitmap(img); // wxWidgetsのウィンドウに表示 wxPaintDC dc(this); dc.DrawBitmap(wxbitmap, 0, 0, true);
} }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // wxWidgetsのアプリケーション作成 class MyApp : public wxApp { public: virtual bool OnInit() { MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(550, 550)); frame->Show(true); return true; } }; ///////////////////////////////////// ///////////////////////////////////// ///////////////////////////////////// // WinMainをマクロで定義 wxIMPLEMENT_APP(MyApp);
// SkDashPathEffectに必要 #include "skia/include/core/SkPathEffect.h" #include "skia/include/effects/SkDashPathEffect.h"

void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setStrokeWidth(5); // 線の太さ paint.setColor(SK_ColorRED); // 線の色 paint.setStyle(SkPaint::kStroke_Style);// 直線を描画 canvas.drawLine(50, 50, 300, 300, paint); // #include "skia/include/effects/SkDashPathEffect.h" const float intervals[] = { 10, 10 }; // 破線で描画 paint.setPathEffect(SkDashPathEffect::Make(intervals,2, 0)); canvas.drawLine(50, 80, 300, 330, paint); }
//SkDiscretePathEffect #include "skia/include/core/SkPath.h" #include "include/effects/SkDiscretePathEffect.h"

void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setStrokeWidth(5); // 線の太さ paint.setColor(SK_ColorRED); paint.setStyle(SkPaint::kStroke_Style); // 直線を描画 auto pathEffect = SkDiscretePathEffect::Make(5.0f, 4.0f); // 乱数シード paint.setPathEffect(pathEffect); SkPath path; path.moveTo(50, 50); // 開始点 path.lineTo(300, 300); // 終了点 // パスを描画 canvas.drawPath(path, paint); }

void draw(SkCanvas& canvas) { // 背景を白にクリア canvas.clear(SK_ColorWHITE); SkPaint paint; paint.setColor(SK_ColorRED); // 線の色を設定 paint.setStrokeWidth(40); // 線の幅を設定 paint.setStyle(SkPaint::kStroke_Style); // 描画スタイルをストロークに設定 paint.setStrokeCap(SkPaint::kButt_Cap); // デフォルト canvas.drawLine(50, 50-20, 300, 300-20, paint); paint.setStrokeCap(SkPaint::kRound_Cap); // 両端を丸くする canvas.drawLine(50, 110, 300, 360, paint); }
SkiaでPng保存するには、SkPngEncorder.hとSkStream.hをインクルードし、SkPngEncoder::Optionsで圧縮などの設定、SkFILEWStream でファイルストリーム作成、SkPngEncoder::Encodeで書き込みを行う。
#pragma comment(lib,"skia.dll.lib") #include "skia/include/core/SkCanvas.h" // SkBitmap #include "skia/include/core/SkBitmap.h" // Png保存に必要 #include "skia/include/encode/SkPngEncoder.h" #include "skia/include/core/SkStream.h" #include <cstdio> #include <vector> // 必要なDLL // skia.dll // jpeg62.dll // zlib1.dll #pragma warning(disable:4996) int main() { ////////////////////////////////////////////////////// SkBitmap bitmap; bitmap.setInfo(SkImageInfo::MakeN32Premul(400, 400)); bitmap.allocPixels(); SkCanvas canvas(bitmap); // 背景を白にクリア canvas.clear(SK_ColorWHITE); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint); ////////////////////////////////////////////////////// // optionsの設定 SkPngEncoder::Options options; options.fZLibLevel = 9; // 圧縮レベル options.fFilterFlags = SkPngEncoder::FilterFlag::kAll; // フィルタの種類 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.png"); SkPngEncoder::Encode(& stream, pixmap, options); return 0; }
Jpegの場合はSkJpegEncoder.hをインクルードする。
#include "skia/include/encode/SkJpegEncoder.h"
後はほぼ同じ。ただしoptionsの指定内容はJpeg用になっている。
SkJpegEncoder::Options options; options.fQuality = 100; // 画質 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.jpg"); SkJpegEncoder::Encode(&stream, pixmap, options);
Webpの場合はSkWebpEncoder.hをインクルードする。
#include "skia/include/encode/SkWebpEncoder.h"
Webpは非可逆圧縮なのでjpegと同じようにfQualityがある。
SkWebpEncoder::Options options; options.fQuality = 100; // 画質 SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char*)bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // bitmapをファイル保存 SkPixmap pixmap(info, pixels, rowBytes); SkFILEWStream stream("output.jpg"); SkWebpEncoder::Encode(&stream, pixmap, options);
skiaを使いたいがビルドに失敗するので妥協してvcpkgで導入してとにかく使う。
適当なディレクトリからgit cloneした後、bootstrap-vcpkg.batを実行。
導入はinstallコマンドで行う。ビルド等が行われた後、includeディレクトリなどが packages\skia_x64-windows\ に入っている。
#pragma comment(lib,"skia.dll.lib") #include "skia/include/core/SkCanvas.h" // SkBitmap #include "skia/include/core/SkBitmap.h" #include <cstdio> #include <vector> // 必要なDLL // skia.dll // jpeg62.dll // zlib1.dll #pragma warning(disable:4996) //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); } int main() {
// skiaの設定
SkBitmap bitmap; bitmap.setInfo(SkImageInfo::MakeN32Premul(400, 400)); bitmap.allocPixels(); SkCanvas canvas(bitmap); // 図形の描画 SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(5); paint.setColor(SK_ColorRED); canvas.drawCircle(200, 200, 100, paint);
// canvasのバッファへアクセス SkImageInfo info = bitmap.info(); unsigned char* pixels = (unsigned char* )bitmap.getPixels(); // 画像データの先頭アドレス size_t rowBytes = bitmap.rowBytes(); // 1行のバイト数 // チャンネル数 int channels = info.bytesPerPixel(); // 画像サイズ int width = info.width(); int height = info.height();
printf("channels %d\n", channels);
// PPMへ出力
std::vector<unsigned char> ppm(width * height * 3); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { size_t pos_ppm = (i * width + j) * 3; size_t pos_sk = (i * rowBytes + j * channels); ppm[pos_ppm + 0] = pixels[pos_sk + 2]; ppm[pos_ppm + 1] = pixels[pos_sk + 1]; ppm[pos_ppm + 2] = pixels[pos_sk + 0]; } } pnmP3_Write("output.ppm", width, height, ppm.data());
}
実は一年くらい公式のビルド方法を試して失敗している。もういい。
できるようにはなったが、想定した手順の再現が思ったようにできていない(環境の初期化に自信がない)。(いつもそうだが今回はいつのも増して)自分用のメモなので、参考にされる方は注意。
・Windows 11 ... いつも使っている Windows 11
・WSL2 ... Windowsの上で動かしているUbuntu
・コンテナ ... docker内で動いている環境。今回はUbuntu。
・Docker ... WSL2の中でコンテナを動かす。導入済みとする。
CGとかゲームやってる人ならすでに入っていると思う。Windows 11 に、自分のグラフィックボード用のドライバを入れた記憶があるなら次の手順へ。
よく注意されるのは、ドライバのインストールはあくまで普通の「Windowsへドライバを入れる作業(公式から.exeを落としてマウスでアイコンをポイントしダブルクリックしインストール画面を開きウィザードに従って次へを押していく)」として行わなければならず、「WSL2でLinuxを使っているからと言ってaptコマンドで入れないように」という所。
公式: https://www.nvidia.co.jp/Download/index.aspx?lang=jp
WSL2上で以下のコマンドを打って、ドライバのバージョンの表記が出れば成功。
なおここでCUDAバージョンが表示されるが、これはこのドライバが対応しているCUDAドライバの最大バージョンとなる。これより新しいCUDAは入れられないという意味。
公式のインストール方法に従って、WSL2上に、aptを使ってインストールする。
公式: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
以下を写経
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
Docker内のUbuntuは、WSL2にインストールされたCUDA Toolkitを参照する形で使用する。
公式へ行く。例えばCUDA 11.8を導入したい場合、以下。
https://developer.nvidia.com/cuda-11-8-0-download-archive
Linux → x86_64 → WSL-Ubuntu → 2.0 → deb(local)
の順で選択するとコマンドが表示されるのでそれをWSL2で実行する。
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-wsl-ubuntu-11-8-local_11.8.0-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-11-8-local_11.8.0-1_amd64.deb
sudo cp /var/cuda-repo-wsl-ubuntu-11-8-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda
CUDAを使うためには -v と --gpusを指定しなければいけない。
-v ... WSL2側のディレクトリをコンテナにマウントする。これにより、WSL2側にインストールしたcudart.so.11.0等をコンテナ側から使えるようになる。
--gpus ... コンテナからGPUを使う設定。allを指定する
-vオプションでは、WSL2側のCUDAのインストールディレクトリを、コンテナ側の指定したパスでアクセスできるようにしている。
まずはコンテナを起動し、
ドライバが読めていることを確認
CUDA Toolkitが使えることを確認
まず、以下のようにpython環境を整えておく。
import numpy as np import matplotlib.pyplot as plt def plot_sample(start, end, step): x = np.arange(start, end, step) y = np.sin(x) plt.plot(x, y) plt.title('Sine Curve') plt.xlabel('x') plt.ylabel('sin(x)') plt.grid(True) plt.show() if __name__ == "__main__": plot_sample(0, 2*np.pi, 0.01)
上記コードを実行しても何も起こらない
VcXsrvはXサーバーで、windows上でLinuxのGUIを表示するのに必要。以下からダウンロード
https://sourceforge.net/projects/vcxsrv/
設定は以下のサイトがとても端的にまとまっている。
https://qiita.com/ryoi084/items/0dff11134592d0bb895c
以下のように設定。Native openglを外す。
最後にSave Configurationで設定ファイル(拡張子.xlaunch)を出力。これを開くとサーバーが起動する。
GUIを表示したいUbuntuに、Xserverがあることを教える設定をする。
vim ~/.bashrc などで、.bashrcを開き、末尾に以下を追記
ここで、MY-HOST-NAMEは自分のパソコンのホスト名を記述。自分のホスト名はPowerShellなどを起動して、ipconfig /all をすると出てくる。
ここで一度、test.pyを実行すると、以下のエラーが出た。
これはtkinterがインストールされていないために起こるらしい。なので以下を実行。
tkinterは、インストール途中、エリアを聞かれるのでAsia、Tokyoを指定。
他に、PySide2が(も)要求されるかもしれない。
あるいはPyQt5が(も)必要かもしれない。
なんと、docker内に作ったubuntu環境にはsudoが入っていない。
というかそもそも気分的にrootで作業したくない。ユーザの追加をしたい。
というわけでLinuxコマンドの復習と再学習を始めたい。
入れ方:
ユーザーを追加した後 visudo コマンドを使いたいが vim がインストールされていないと使えないのでvimを入れておく。
ユーザー切替は
これだけだと、sudoを使おうとしたときに
と言われるので、sudoersファイルを編集しなければならない。
whoamiを実行すると現在のユーザがわかるので、rootであることを確認する
そしてvisudoで/etc/sudoersを編集
下のほうにある、# User privilege specification のrootの下に、使いたいユーザを入れる
wq;でviから出ると追加したユーザでsudoが使えるようになっている。
毎度この設定をするのも面倒なので、今回の設定をしたUbuntuをdockerイメージとして作成し、いつでもコンテナを作れるようにしておく。なおイメージ名は小文字でないといけない
docker ps -a でコンテナ名を確認してから、 docker commitを行う。
docker commit u2204base my_image_ubuntu_2204
投稿順序を間違えた。こっちが先。
Docker Desktopを導入した。正直色々調べながらどうにか入れたので理解しきれていない部分もある。ヒント程度の意味でまとめている。
Windows上でDockerを使うには
・WSL2 (Windows上でUbuntuを使う仕組み)
・Ubuntu (Windows上にMicrosoft Storeからインストール)
・Docker Desktop (本体)
の三つが必要。
順番は前後してもいい(私がやった順番は3124だがちゃんと動いた)。
wslはWindows上でLinuxを動かすための仕組みのこと。
コマンドプロンプトまたはPowerShellを開き、以下を実行する
そもそもwslが入っていない場合、インストールする必要がある。
wslにはバージョン1と2がある。基本的に2のほうが優れているので、Ubuntu環境を使うときにデフォルトで2を使うように設定しておく
Ubuntuを入れたあと、Ubuntuを実行しようとするとエラーが出る場合がある。これはupdateで解決する
WSLのおかげでWindows上でUbuntuを実行できる。Microsoft Storeへ行ってUbuntuを検索。バージョン違いが複数出てくる。特にこだわりがなければバージョン未表記のものを選べば最新版が入る。
インストール後、「開く」でUbuntuの設定が走る。Ubuntu上で使うユーザ名とパスワードを入力する。
もしここで以下のエラーが出るようなら上に書いたように wsl --updateを実行。
Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x800701bc
Error: 0x800701bc WSL 2 ???????????? ??????????????????????? https://aka.ms/wsl2kernel ?????????
Press any key to continue...
うまくいけばUbuntuのターミナルが使えるようになる。
以下へ行き、Docker Hub からダウンロード からダウンロード・インストールする。
https://docs.docker.jp/docker-for-windows/install.html
注意:インストール後、docker subscription service agreement という、利用規約に合意しろという意味のウィンドウが出てくるので、Acceptする。これをしないでCloseするとDockerが動かない。
コマンドプロンプト、PowerShell、Ubuntuのターミナルのどれでもいいのでdockerコマンドを実行
なお、私はここで
The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.
For details about using Docker Desktop with WSL 2, visit:
https://docs.docker.com/go/wsl2/
に苦しめられた。調べるとDocker Desktopの設定から
[Resoures] → [WSL integration] → [Enable integration with additional distros:]
でUbuntuをOnにすれば解決、と言われるのだがその項目すらない。
私の場合は上で書いたように、docker subscription service agreementでAcceptしていなかったのでDockerが動いていなかった。
https://ascii.jp/elem/000/004/127/4127643/
docker ... 仮想環境作成ソフト
イメージ ... 仮想環境のひな型
コンテナ ... イメージから作った実際に動く仮想環境
という関係になっている。環境構築の手順としてはまずイメージをダウンロードし、それを元にコンテナを作る。今回はubuntuの環境を作る。
ダウンロードの前に、いまどのイメージを使えるのかを確認する。
結果。まだ何もないのでイメージ一覧には何も出てこない。
次にイメージを取得。ネットにつながっていれば、docker pullでイメージをダウンロードできる。
これは
の順番で記述する。例えばバージョン18.04が欲しければ、 docker pull ubuntu:18.04 と記述する。最新版が欲しい場合は docker pull ubuntu:latest と、latest指定をする。
もう一度docker imagesをしてみると、イメージ一覧にubuntuのlatestが追加されている。
イメージがダウンロードできたので、このubuntu:latestを元にコンテナ(=仮想環境)を作る。
コンテナを作るには、docker create を使う。
オプションは、-t,-i両方を指定しておいたほうがいい。一応個別に設定できるが(詳しくないのに)つけないでいると仮想環境が動かなかったりI/Oができなかったりする。
-tオプション ... 仮想環境で仮想ターミナルを使う。
-iオプション ... 仮想環境と標準入出力を繋げる。コマンドラインが使用可能になる
作った仮想環境を動かすにはdocker startを使う。
-a ... 標準出力,エラー出力を接続
-i ... コンテナと対話できるようにする
-aはコンテナの出力を見ることができる(こちらから入力できない)
-aだけで接続してしまった場合、こちらから入力できないので、Ctrl+Cで帰ってくる以外に何もできなくなる。
終了する場合は、exitすれば自動的に終了する。
docker psは、何もつけないと現在動作しているコンテナしか表示されないので、-aをつけて停止中のコンテナも表示する
ただし、Ctrl+Cでコンテナから出た場合、終了していないので docker stop 環境名 をする必要がある。
docker run は、コンテナを作成し、その後起動まで行う。便利といえば便利だが、docker runする度に新たなコンテナが新規作成されるので注意が必要。
docker run でただ起動すると名前が勝手に付けられる(NAMES)。名前を付けたいときは --nameを指定。
コンテナにイメージが使われている場合削除できないので、先にdocker rm で、削除したいイメージから作成したコンテナをすべて削除しておく必要がある。
docker rmi ubuntu:latest