OpenCV 2系でGPUで膨張処理をさせる方法

OpenCV 2系でGPUで膨張処理をさせる方法で詰まったので書いておく。最後に検証に使った全コードがあるのでどうぞ。

CPUで膨張処理をさせるとき、さくっと書けば次のようになる。

cv::Mat src, dst;
int width = 256;
int height = 256;
src = cv::Mat::eye(cv::Size(width, height), CV_8U);
cv::dilate(src, dst, cv::Mat::ones(3, 3, CV_8U));
std::cout << cv::countNonZero(dst) << " cpu dilate w/o rect" << std::endl;

この結果は、 256+255*2+254*2=1274 となる。 たとえば、周辺1ピクセルを膨張処理させなかった場合は、

cv::Rect rect = cv::Rect(1, 1, width - 2, height - 2);
cv::dilate(src(rect), dst(rect), cv::Mat::ones(3, 3, CV_8U));

この結果は、 256+255*2+254*2-5*2=1264 となる。

GPU版では、領域外チェックが甘いため、全ての領域を処理させたとき、領域外のメモリを含めて計算してしまうバグ(仕様)がある。 それを検証するために、 cv::gpu::dilate と、 cv::gpu::getMaxFilter_GPU と、cv::gpu::createBoxFilter_GPU を比較してみた。 それぞれ、全領域で処理させた場合 w/o rect と、 rect で周辺1ピクセルを無視した場合 with rect を行った。 結果は次のとおり。

処理方法 countNonZeroの結果
gpu dilate w/o rect 1264
gpu dilate with rect 1249
gpu max w/o rect 1278
gpu max with rect 1264
gpu box w/o rect 1259
gpu box with rect 1264

w/o rect の期待値は1274なので、いずれも正しい答えを返していない。一方で、 with rectcv::gpu::getMaxFilter_GPU と、cv::gpu::createBoxFilter_GPU で正しい答えを返している。 この2つ以外は、おそらく環境依存で値が変わりうると考えられる。ここで、BoxFilterを使うときは、下記の検証用コードでもそそしているとおり、要素を Expansion の二乗より大きい値にしておく必要がある。さらに、最後に適切な値に閾値処理する必要がある。これらの手間を考えると、 cv::gpu::getMaxFilter_GPUwith rect で使うのが最もシンプルな方法であろう。

なかなか難解なライブラリである。

さて、検証用全コード

#include <opencv2/opencv.hpp>
#include <opencv2/gpu/gpu.hpp>

using namespace cv;

int main() {

    cv::gpu::setDevice(0);

    int gpudeviceid = cv::gpu::getDevice();
    cv::gpu::DeviceInfo dev(gpudeviceid);
    string gpu_name = dev.name();

    cv::gpu::Stream stream;
    int Expansion = 3;

    Ptr<cv::gpu::BaseFilter_GPU> fb;
    fb = cv::gpu::getMaxFilter_GPU(CV_8UC1, CV_8UC1, Size(Expansion, Expansion));

    cv::Ptr<gpu::FilterEngine_GPU> filter;
    filter = gpu::createBoxFilter_GPU(CV_8U, CV_8U, cv::Size(Expansion, Expansion));

    cv::gpu::GpuMat gsrc, gdst;
    cv::Mat src, dst;

    int width = 256;
    int height = 256;
    src = cv::Mat::eye(cv::Size(width, height), CV_8U) * 9;
    gsrc.upload(src);

    cv::Rect rect = cv::Rect(1, 1, width - 2, height - 2);

    {
        cv::dilate(src, dst, cv::Mat::ones(3, 3, CV_8U));
        std::cout << cv::countNonZero(dst) << " cpu dilate w/o rect" << std::endl;
    }

    {
        dst = cv::Scalar(0);
        cv::dilate(src(rect), dst(rect), cv::Mat::ones(3, 3, CV_8U));
        std::cout << cv::countNonZero(dst) << " cpu dilate with rect" << std::endl;
    }

    src = cv::Scalar(0);
    {
        gdst.upload(src);
        cv::gpu::dilate(gsrc, gdst, cv::Mat::ones(3, 3, CV_8U));
        std::cout << cv::countNonZero(dst) << " gpu dilate w/o rect" << std::endl;
    }

    {
        gdst.upload(src);
        cv::gpu::dilate(gsrc(rect), gdst(rect), cv::Mat::ones(3, 3, CV_8U));
        std::cout << cv::gpu::countNonZero(gdst) << " gpu dilate with rect" << std::endl;
    }

    {
        gdst.upload(src);
        (*fb)(gsrc, gdst);
        std::cout << cv::gpu::countNonZero(gdst) << " gpu max w/o rect" << std::endl;
    }

    {
        gdst.upload(src);
        (*fb)(gsrc(rect), gdst(rect)); //おすすめの方法
        std::cout << cv::gpu::countNonZero(gdst) << " gpu max with rect" << std::endl;
    }

    {
        gdst.upload(src);
        filter->apply(gsrc, gdst);
        std::cout << cv::gpu::countNonZero(gdst) << " gpu box w/o rect" << std::endl;
    }

    {
        gdst.upload(src);
        filter->apply(gsrc, gdst, rect);
        std::cout << cv::gpu::countNonZero(gdst) << " gpu box with rect" << std::endl;
    }

    fb.release(); // no error

    stream.waitForCompletion();
    std::cout << "Free memory rate = " << dev.freeMemory()*100.0 / dev.totalMemory() << " [%] at Device = " << gpudeviceid << " at first" << std::endl;
    return 0;
}

WSL (Ubuntu系)における便利なコマンド一覧

自分用のメモとして随時更新

OSのバージョン確認

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

インストールパッケージをアップデート。これをしないと何もできない。

$ sudo apt update

ビルドツールを一括でインストール。gccやmakeなどがインストールされる。

$ sudo apt install build-essential

ファイルをダウンロード

$ wget https://ファイル名

SRIM 2013を日本語版 Windows 10 64bitで動かすためのメモ

とても個人的なメモなので、使えなくても泣かないお約束で。

SRIM本体は下記のURLからインストールする。

http://www.srim.org/SRIM/SRIMLEGL.htm

Msvbvm50.dll がないと怒られるので、下記URLからMsvbvm50.exeダウンロードし実行する

https://support.microsoft.com/ja-jp/help/180071/file-msvbvm50-exe-installs-visual-basic-5-0-run-time-files

RICHTX32.OCX がなんとかってエラーが出るので、下記URLから Visual Basic 6.0 Service Pack 6 をダウンロード

Download Visual Basic 6.0 Service Pack 6 from Official Microsoft Download Center

適当なフォルダに展開し、さらにフォルダ内の RichTx32.CAB を展開する。

RICHTX32.OCX というファイルがあるので、 C:\Windows\SysWOW64 にコピーし、管理者権限のコマンドプロンプト

cd C:\Windows\SysWOW64
regsvr32 RICHTX32.OCX

を実行する。同様の処理をさらに4つのファイルで行う

  • COMDLG32.CAB
  • MSFLXGRD.CAB
  • TABCTL32.CAB
  • COMCTL32.CAB
regsvr32 COMDLG32.OCX
regsvr32 MSFLXGRD.OCX
regsvr32 TABCTL32.OCX
regsvr32 COMCTL32.OCX

これで、 SR.exe (静的にdE/dx やStragglingを計算するソフト)や、 TRIM.exe (動的に粒子を打ち込むソフト)が動くはずである。

TRIM.exe を実行するための TRIM.in ファイルを作るための TIN.exe は動かない。 SRIM.exe に特段の機能は無い。

pythonでファイルを1行ずつ読み込む方法

行頭に # 付きはコメント行、空白行は読み飛ばす

def read_txt(filename):
    lines = []
    for line in open(filename, 'r'):
        if len(line) == 1:
            continue
        if line[0] == "#":
            continue
        lines.append(line)
    return lines
def read_data12(filename):
    items = {}
    for line in open(filename, 'r'):
        if len(line) == 1:
            continue
        if line[0] == "#":
            continue
        item_list = line.split()
        items["data1"] = float(item_list[0])
        items["data2"] = float(item_list[1])
    return items
data1 = []
data2 = []
for line in open(filename, 'r'):
    if len(line) == 1:
        continue
    if line[0] == "#":
        continue
    item_list = line.split()
    data1.append(float(item_list[0]))
    data2.append(float(item_list[1]))

print(data1)
print(data2)

Portable版のVisual Codeを更新(アップデート)する方法

記憶喪失になった時用のメモ

  • zipをダウンロードする
  • zipを解凍する
  • 展開先のVSCode用のフォルダの data 以外を削除する
  • 解凍したデータを削除したフォルダに入れる

以上。 data を消してしまうと泣くので気をつけよう。

C# WPFで グリッドマーク上にTextBlockやRectangleを配置する

供養

var textBlock = new TextBlock
{
    HorizontalAlignment = HorizontalAlignment.Center,
    VerticalAlignment = VerticalAlignment.Center,
    Text = text,
    FontSize = 0.1
};
var rectangle = new Rectangle
{
    Stroke = Brushes.Black,
    StrokeThickness = 0.3
};
Grid.SetColumn(textBlock, x);
Grid.SetRow(textBlock, y);
Grid2.Children.Insert(0, textBlock);

Grid.SetColumn(rectangle, x - 1);
Grid.SetColumnSpan(rectangle, 3);
Grid.SetRow(rectangle, y - 1);
Grid.SetRowSpan(rectangle, 3);
Grid2.Children.Insert(0, rectangle);

C#でpython 3のスクリプトを実行する方法

英語をすらすら読める人はここを読めば全て解決する。 code.msdn.microsoft.com

方法としては、C#におけるプロセス間通信の一つである Process を使って、pythonスクリプトを実行し、その標準出力(コンソール出力)をストリームで受け取るという方法。pythonの戻り値そのものを取得できるわけではないので、ある関数だけ叩くということはできず、python側ではmainに相当する部分と戻り値に相当するprint文を書いておく必要がある。以下は、上記のURLのサンプル例を自分なりに修正したもの。

Program.cs

using System;
using System.Diagnostics;
using System.IO;

namespace ConsoleApp4_python
{
    class Program
    {
        static void Main(string[] args)
        {
            string myPythonApp = @"C:\Users\Masahiro\source\repos\project\interprocess\test.py";

            int x = 2;
            int y = 5;

            var myProcess = new Process
            {
                StartInfo = new ProcessStartInfo("python.exe")
                {
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    Arguments = myPythonApp + " " + x + " " + y
                }
            };

            myProcess.Start();
            StreamReader myStreamReader = myProcess.StandardOutput;
            string myString = myStreamReader.ReadLine();
            myProcess.WaitForExit();
            myProcess.Close();

            Console.WriteLine("Value received from script: " + myString);
        }
    }
}

test.py

import sys 
 
x = int(sys.argv[1])  
y = int(sys.argv[2]) 
 
print(x + y)

実行したら次のようになる

Value received from script: 7

Windows 10でC++とpythonでROOT6 (CERN)を動かそう

最終更新: 2018/10/29

Windows用のROOT6はプレビュー版がリリースされています。これは、Visual Studio 2017が必須です。無料のCommunity版でも良い。しかし、Visual StudioC++コンパイラーのバグにより、現状プレビュー版ROOT6用は正常に動きません。

Assertion failed occurred when executing compiled exe with Visual Studio 2017 - ROOT - ROOT Forum

バグレポートとbellenot氏の返答を参照。

現状、Windowsで仮想環境なしにROOT6を動かす方法は、WSL(Windows Subsystem for Linux)のみです。

  • WSL (Windows Subsystem for Linux) で、Ubuntuの最新版を入れる (18.04 LTS)
  • ROOT 6が必要とするパッケージ類をインストールする
  • Ubuntu用のビルド済み ROOT 6をインストールする
  • Windows用のXサーバー「Xming」をインストールする

上記の方法は、それぞれ日本語でもかなりの記述がネットで見つかるので、慣れた方ならできるかと思います。

ROOTのインストール方法の日本語訳

Downloading ROOT | ROOT a Data analysis Framework

僕の流儀はZIPからインストールなのでそちらだけ。

伝統的な変種(筆者注: 変種ってどう訳すの?)。7zip(筆者注: おすすめなので入れておくべし)などで解凍する。ROOTはMicrosoft Visual Studioコマンドプロンプト(スタートメニューのプログラムのVisual Studio 2017 VS 2017 用)から始める。ROOTを C:\root にインストールしたとき、ROOTを使う前に必要な環境変数をセットアップするために C:\root\bin\thisroot.bat を実行せよ。

重要なインストール手法

  • システムにインストールされたのと完全に同じバージョンのVisual Studioでビルドされた(筆者注: 今回はVS2017)バイナリをダウンロードする必要がある。
  • ブランク文字を含む(筆者注: 日本語のパスを含む)ディレクトリにZIPを解凍してはならない。
  • パフォーマンスが重要な場合(筆者注: 基本的に必要である)は、リリース版を入手せよ。
  • コードをデバッグ(筆者注: ここでのデバッグは、ROOTそのもののデバッグを意味する。コードがコンパイルできないなどの問題の多くは、ROOTではなく君が書いたコードにバグがある)する場合、ROOTのデバッグ用ビルドが必要。マイクロソフトの制限のため、リリース版とデバッグ版を混在はできない。

Surface book 2についてくる Office Home & Business 2016は、Office 365に契約している場合ゴミになるのか?

ゴミです。

本製品は、本製品が付属していたパソコンでのみ使用できます。本製品のみをネットオークションなどで転売したり、他のパソコンで使用することはライセンス契約違反です。

って書いてあるし。

せっかくOffice 365でMicrosoftに貢ごうと思っても、目の前に未使用の Office Home & Business 2016 があるとなんか躊躇するよね、何なんだろうこの商法は。大嫌い。

タイトル未定

    System.Net.WebException
    - ログオン失敗: ユーザー名を認識できないか、またはパスワードが間違っています。

でOne

ドライブには接続されていたが、IPアドレスで直接行ける場所にはログイン情報が消えていた様だ。

C# WPF で BitmapImage (BitmapSource) のピクセルにアクセスし書き換える方法

BitmapImage (BitmapSource) のピクセルにアクセスし書き換える方法

例として、コントラストを変える処理を紹介する。

BitmapSource ApplyContrast(BitmapSource image, double contrast)
{
    if (contrast == 0) return image;
    var bitmap = new FormatConvertedBitmap(image, PixelFormats.Gray8, null, 0);
    int width = bitmap.PixelWidth;
    int height = bitmap.PixelHeight;
    byte[] pixcels = new byte[width * height];
    int stride = (width * bitmap.Format.BitsPerPixel + 7) / 8;
    bitmap.CopyPixels(pixcels, stride, 0);
    var offset = 256 * contrast;
    for (int x = 0; x < pixcels.Length; x++)
    {
        pixcels[x] = (byte)Math.Min(Math.Max(pixcels[x] * (1 + contrast) - offset, 0), 255);
    }
    return BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray8, null, pixcels, stride);
}

そんなに早くないから大量の処理には向かないよ

Portable版 Visual Studio Code (VSCode)における Open with Codeの追加方法 (Windows)

Portable版 VS CodeC:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe に展開したとする。 このままでは右クリックしたときの便利な設定を使えないので、手動でレジストリに追加する。

ファイルを右クリックしたときに有効になる設定

  • HKEY_CLASSES_ROOT*\shell\Open with Code
    既定に Edit with Code
    Icon 文字列に C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe,0
  • HKEY_CLASSES_ROOT*\shell\Open with Code\command 既定に "C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe" "%1"
    または、C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe "%W" "%1"

フォルダを右クリックしたときに有効になる設定

  • HKEY_CLASSES_ROOT\Directory\shell\VSCode
    既定に Open with Code
    Icon文字列に C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe,0
  • HKEY_CLASSES_ROOT\Directory\shell\VSCode\command
    既定に "C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe" "%1"

フォルダ内の空白部分を右クリックしたときに有効になる設定

  • HKEY_CLASSES_ROOT\Directory\Background\shell\VSCode
    既定に Open with Code
    Icon文字列に C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe,0
  • HKEY_CLASSES_ROOT\Directory\Background\shell\VSCode\command
    既定に "C:\Users\Masahiro\OneDrive\Software\VSCode\Code.exe" "%V"

%1 は1個目の引数、%V はそのファイルのフルパス、%W は親フォルダのフルパス が帰ってくるらしい。 qiita.com

ファイル単体を開きたい場合の引数は %1 、ファイルをフォルダ内で開きたい場合の引数は "%W" "%1" とするとよいだろう。