ポート名を指定して既存のプリンタドライバを削除する

こちらのように”rundll32.exe printui.dll,PrintUIEntry ~”を使用すればコマンドラインからプリンタをインストールする事で設定の共通化を出来るが、既に同一ポート名でプリンタが導入されていた場合に複数のプリンタドライバをインストール出来てしまう。同一ポートを使用するプリンタドライバをインストールしてしまうと、設定⇒デバイス⇒プリンタとスキャナーで表示したときに、一つに集約されてしまい、非常に扱い難くなる。

そこで事前に以下のPowershellスクリプトでポート名を指定して既存プリンタを削除しておく事で、同一ポート名で重複登録することを防ぐとよい。

#RemovePrinterWithPort.ps1
$removePort = $args[0];
$printerNameKey = "プリンター名";
$portNameKey = "ポート名";

$printerName = "";
$portName = "";
cscript $env:SystemRoot\System32\Printing_Admin_Scripts\ja-JP\prnmngr.vbs -l | ForEach-Object {
    if ($_ -match ('^' + $printerNameKey + '.*'))
    {
        $printerName = $_.Replace(($printerNameKey + " "), "");
    }

    if ($_ -match ('^' + $portNameKey + '.*'))
    {
        $portName = $_.Replace(($portNameKey + " "), "");
        if ($portName -eq $removePort)
        {
            ("Remove Printer : " + $printerName);
            cscript $env:SystemRoot\System32\Printing_Admin_Scripts\ja-JP\prnmngr.vbs -d -p $printerName;
        }
    }
}

exit;

上記スクリプトをRemovePrinterWithPort.ps1で保存し、以下のコマンドでコマンドプロンプトから呼び出す。

 powershell.exe -Command .\RemovePrinterWithPort.ps1 IP_192.168.0.10

コマンドプロンプトからプリンタドライバをインストールする(Canon LIPS LX)

コマンドプロンプト(BATファイル)からサイレントにプリンタドライバをインストール手順です。コマンドプロンプトからインストール出来ると、キッティングやメンテナンスの手間を大幅に減らせます。CanonのLIPS LXドライバを例としていますが、他社のドライバでも本質的には同じです。

CanonのWEBサイトからダウンロードしたインストーラを起動し、展開されたファイルから.\x64\Driver下の以下のファイルを取り出します。

  • cnlb0m.cat
  • CNLB0MA64.INF
  • gpb0.cab

INFファイルの中味を確認する。以下の”Canon Generic Plus LIPSLX”がドライバ名にあたる。

; Canon Generic Plus LIPSLX printer INF for Microsoft Windows (x64)
; Copyright CANON INC. 2017
; CNLB0MA64.INF

[Version]
Signature="$Windows NT$"
Provider=%CANON%
ClassGUID={4D36E979-E325-11CE-BFC1-08002BE10318}
Class=Printer
DriverVer=06/26/2023,2.90.0.0
CatalogFile=CNLB0M.CAT

[Manufacturer]
"Canon" = Canon,NTamd64,NTamd64.6.0

;64-bit x64
[Canon.NTamd64]
"Canon Generic Plus LIPSLX"             = GPBDL,,1284_CID_CA_XPS_OIP
"Canon Generic Plus LIPSLX"             = GPBDL,,1284_CID_CA_UFRII_COLOR_OIP
"Canon Generic Plus LIPSLX"             = GPBDL,,1284_CID_CA_UFRII_BW_OIP
"Canon Generic Plus LIPSLX"             = GPBDL,CanonGeneric_V3_PrinC2DB

以下の様なバッチファイルを作成する。
1.prnmngr.vbsで既存プリンタ設定を列挙し既にインストール済みなら実行しない。
2.prnport.vbsでプリンタポートを作成
3.rundll32.exe printui.dll,PrintUIEntry~でドライバをインストールする。Canon以外のプリンタの場合には/fオプション以下のINFファイル名、/mオプション以下のプリンタドライバ名が変わる事になる。/bオプション以下は任意のプリンタ名となる。

set exe_dir=%~dp0

rem IPアドレス、ポート名を格納
set ip_addr=192.168.0.10
set ip_port=IP_192.168.0.10
set prname=iR-ADV C5850

rem ログ出力
set log_file=install.log
set driver_path=%exe_dir%

rem プリンタインストール済みかチェック
set prnmngr=C:\Windows\System32\Printing_Admin_Scripts\ja-JP\prnmngr.vbs
cscript %prnmngr% -l | findstr /l /C:"%prname%"
if %ERRORLEVEL%==0 GOTO END

:INSTALL
rem ポートの追加
set prnport=C:\Windows\System32\Printing_Admin_Scripts\ja-JP\prnport.vbs
cscript %prnport% -a -s %computername% -r %ip_port% -h %ip_addr% -o raw -n 9100 >> "%exe_dir%%log_file%

rem プリンタドライバの追加
rundll32.exe printui.dll,PrintUIEntry /if /f "%driver_path%\CNLB0MA64.INF" /v "Windows 10" /m "Canon Generic Plus LIPSLX" /r "%ip_port%" /b "%prname%"

:END

BIG-IP EDGE CLIENTが正常に動作しない

BIG-IP EDGE CLIENTが正常に動作しない場合、接続時にバックグラウンドで自動的にダウンロードするモジュールが壊れている場合があります。BIG-IP EDGE CLIENTは必要なファイルを”C:\Windows\Downloaded Program Files”にダウンロードしますので、管理者権限で当該フォルダの中にあるファイルを全て削除してから再実行してみてください。

当該フォルダはBIG-IP EDGE CLIENT以外のアプリケーションも使用している可能性があります。ですが仮に削除しても、実行時に再度ダウンロードするはずなので問題はありません。

SeleniumからChromeのインスタンス作成時にエラー

OpenQA.Selenium.WebDriverException: 'disconnected: Unable to receive message from renderer
  (failed to check if window was closed: disconnected: not connected to DevTools)
  (Session info: chrome=119.0.6045.124)'

C#環境でSeleniumからChromeのインスタンスを作成しようとすると上記のエラーが発生する。

この場合は下記の通り”–no-sandbox”オプションを追記すると問題が解決する場合がある。どうもコンソールリダイレクトのパイプライン処理などが引っかかる場合があるらしい。

using OpenQA.Selenium.Chrome;
using OpenQA.Selenium;
・・・
var options = new OpenQA.Selenium.Chrome.ChromeOptions();
options.AddArgument("--no-sandbox");
var newWebDriver = new ChromeDriver(options);

sandboxはWEB閲覧中のScript動作などの安全性を担保する上で重要な機能になる。接続先WEBサイトに不正なScriptが置かれていた場合、損失を受ける可能性もあるので注意が必要になる。Firefoxなど別のブラウザに移行した方が良いのかもしれない。

OpenTKとC#でOpenCLプログラミング

C#でお手軽にOpenCLを使ったプログラムを作れないかと思い、OpenTKを使用。

OpenTK 3.xではOpenCLの機能がClooとして独立したようですが、現行のOpenTK 4.xでは再びOpenCLが統合されています。

まずはNugetパッケージマネージャからOpenTK 4.7.xをプロジェクトに追加します。

サンプルプログラムはhttps://github.com/opentk/opentk/tree/master/tests/OpenToolkit.OpenCL.Testsにあるので参照。

using OpenTK.Compute.OpenCL;
using System.Text;
CL.GetPlatformIds(0, null, out uint platformCount);
CLPlatform[] platformIds = new CLPlatform[platformCount];
CL.GetPlatformIds(platformCount, platformIds, out _);
Console.WriteLine(platformIds.Length);
foreach (CLPlatform platform in platformIds)
{
    Console.WriteLine(platform.Handle);
    CL.GetPlatformInfo(platform, PlatformInfo.Name, out byte[] val);
    Console.WriteLine(System.Text.Encoding.ASCII.GetString(val));
}
foreach (IntPtr platformId in platformIds)
{
    CL.GetDeviceIds(new CLPlatform(platformId), DeviceType.All, out CLDevice[] deviceIds);
    CLContext context = CL.CreateContext(IntPtr.Zero, (uint)deviceIds.Length, deviceIds, IntPtr.Zero, IntPtr.Zero, out CLResultCode result);
    if (result != CLResultCode.Success)
    {
        throw new Exception("The context couldn't be created.");
    }
    string code = @"
                __kernel void add(__global float* A, __global float* B,__global float* result, const float mul)
                {
                    int i = get_global_id(0);
                    for (int j =0;j < 60000;j++)
                    {
                        for (int k =0;k < 60000;k++)
                        {
                            result[i] = (A[i] + B[i])*mul;
                        }
                    }
                }";
    CLProgram program = CL.CreateProgramWithSource(context, code, out result);
    CL.BuildProgram(program, (uint)deviceIds.Length, deviceIds, null, IntPtr.Zero, IntPtr.Zero);
    CLKernel kernel = CL.CreateKernel(program, "add", out result);
    int arraySize = 20;
    float[] A = new float[arraySize];
    float[] B = new float[arraySize];
    for (int i = 0; i < arraySize; i++)
    {
        A[i] = 1;
        B[i] = i;
    }
    CLBuffer bufferA = CL.CreateBuffer(context, MemoryFlags.ReadOnly | MemoryFlags.CopyHostPtr, A,
        out result);
    CLBuffer bufferB = CL.CreateBuffer(context, MemoryFlags.ReadOnly | MemoryFlags.CopyHostPtr, B,
        out result);
    float[] pattern = new float[] { 1, 3, 5, 7 };
    CLBuffer resultBuffer = new CLBuffer(CL.CreateBuffer(context, MemoryFlags.WriteOnly,
        new UIntPtr((uint)(arraySize * sizeof(float))), IntPtr.Zero, out result));
    try
    {
        CL.SetKernelArg(kernel, 0, bufferA);
        CL.SetKernelArg(kernel, 1, bufferB);
        CL.SetKernelArg(kernel, 2, resultBuffer);
        CL.SetKernelArg(kernel, 3, -1f);
        CLCommandQueue commandQueue = new CLCommandQueue(
                CL.CreateCommandQueueWithProperties(context, deviceIds[0], IntPtr.Zero, out result));
        CL.EnqueueFillBuffer(commandQueue, bufferB, pattern, UIntPtr.Zero, (UIntPtr)(arraySize * sizeof(float)), null,
            out _);
        //CL.EnqueueNDRangeKernel(commandQueue, kernel, 1, null, new UIntPtr[] {new UIntPtr((uint)A.Length)},
        //	null, 0, null,  out CLEvent eventHandle);
        CL.EnqueueNDRangeKernel(commandQueue, kernel, 1, null, new UIntPtr[] { new UIntPtr((uint)A.Length) },
            null, 0, null, out CLEvent eventHandle);
        CL.Finish(commandQueue);
        CL.SetEventCallback(eventHandle, (int)CommandExecutionStatus.Complete, (waitEvent, data) =>
        {
            float[] resultValues = new float[arraySize];
            CL.EnqueueReadBuffer(commandQueue, resultBuffer, true, UIntPtr.Zero, resultValues, null, out _);
            StringBuilder line = new StringBuilder();
            foreach (float res in resultValues)
            {
                line.Append(res);
                line.Append(", ");
            }
            Console.WriteLine(line.ToString());
        });
        //get rid of the buffers because we no longer need them
        CL.ReleaseMemoryObject(bufferA);
        CL.ReleaseMemoryObject(bufferB);
        CL.ReleaseMemoryObject(resultBuffer);
        //Release the program kernels and queues
        CL.ReleaseProgram(program);
        CL.ReleaseKernel(kernel);
        CL.ReleaseCommandQueue(commandQueue);
        CL.ReleaseContext(context);
        CL.ReleaseEvent(eventHandle);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
        throw;
    }
}

GPU上で実行されるプログラムはC言語で記述できます。

拍子抜けするぐらい簡単にOpenCLを使用したプログラムを作れるんですね。

どうしてもGPUへのデータ転送がボトルネックになりやすいので、高速化するのは中々難しいですが、機会があれば活用したいところ。

Docker上で実行しているNextCloudで”Updates between multiple major versions and downgrades are unsupported. Update failed.”が発生してアップデートに失敗する。

Dockerを使ってNextCloudを運用して居る。久しぶりにNextCloudバージョン画面を見たところ、「保守終了したバージョン」との警告が。あわててアップデートを行ったところ、ずっとメンテナンスモードから戻らなくなってしまった。そう、うっかりとversion 23→version 25にアップデートを掛けてしまったため、処理が正常に終了せずに環境が壊れてしまったのだ。

以下のコマンドを実行してメンテナンスモードを強制的に解除してNextCloudをWERBで開くがUpgradeの確認画面になり、そしてUpgradeは失敗してしまう。

$ sudo docker exec -u www-data <container name> php occ maintenance:mode --off

以下のコマンドを実行してコマンドラインからUpgradeを実施するが、「Updates between multiple major versions and downgrades are unsupported. Update failed.」のエラーになってしまう。

$ sudo docker exec -u www-data <container name> php occ upgrade

リカバリ方法がないかと探していたところ「How to fix an accidental Nextcloud docker image update」にたどり着いたので、こちらの方法でリカバリを試みることにする。

まずhtml/config/config.phpを開き、正確なバージョンを確認する。ここでは「’version’ => ‘23.0.0.10’,」とあるとおり、23.0.0.10が正確なージョンになる。

  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'mysql',
  'version' => '23.0.0.10',
  'overwrite.cli.url' => 'https://example.net',
  'dbname' => 'nextcloud',

次にhtml/version.phpを編集する。既にversion 25のファイルに置き換わってしまっているため、これを書き換えて23→24へのアップグレードを通すために、$OC_Versionをarray(25,0,2,3)からarray(23,0,0,10)に、$OC_VersionStringを’25.0.2’から’23.0.0’に、array (‘24.0’ => true, ‘25.0’ => true,)をarray (‘23.0’ => true, ‘24.0’ => true,)に、下記の通りに書き換えます。

<?php
$OC_Version = array(23,0,0,10);
$OC_VersionString = '23.0.0';
$OC_Edition = '';
$OC_Channel = 'stable';
$OC_VersionCanBeUpgradedFrom = array (
  'nextcloud' =>
  array (
    '23.0' => true,
    '24.0' => true,
  ),
  'owncloud' =>
  array (
    '10.11' => true,
  ),
);
$OC_Build = '2022-12-08T11:32:17+00:00 9c791972de9c2561a9b36c1a60e48c25315ecca5';
$vendor = 'nextcloud';

続いてdocker-compose.ymlを編集します。下記のようにnextcloud:24のようにアップデート先となる旧バージョンのdockerイメージを指定します。

  nextcloud:
    image: nextcloud:24

これで23→24にアップデートする環境が整うので、以下のコマンドを実行してNextCloudを起動し、23から24へのアップデートを実行します。

$ sudo docker-compose up --build -d
$ sudo docker exec -u www-data <container name> php occ upgrade

version 24へのアップデートが正常に終了したら、再びdocker-compose.ymlを編集して元に戻します。その後に再び下記のコマンドを実行すれば、24から25へのバージョンアップを行えます。

$ sudo docker-compose up --build -d
$ sudo docker exec -u www-data <container name> php occ upgrade

MNPで携帯電話を乗っ取りSMS認証を突破される

さっきまで使えてたスマホ、通話音が…しない 勝手に解約されたかも 被害男性の証言

偽造運転免許証をもちいて携帯電話のMNPを申込みんで携帯電話を乗っ取り、オンラインバンクのSMS認証を突破されてしまうという問題が多数発生しているらしい。記事の中でも「パスワードを使い回さないなど、やはり個人の対策が重要だ」と書かれているとおり、一番の問題はパスワードを適切に管理出来て居なかった事にある。

SMSを使用した多要素認証は、NIST(米国立標準技術研究所)でも条件付き推奨としている。推奨するための条件は、「他の認証方式と併用すること」「代替の認証方法を用意すること」「危険性の評価し公表すること」三つだ。SMSは電話会社での管理が適切である事を前提としており、技術的あるいは社会的方法で容易に突破されうる事が分かっているからだ。

SMSは攻撃に弱い認証方法であるという前提を理解して、正しくパスワードによる認証を使う事が大事です。「サービス毎に異なるパスワードを使う」「パスワードはランダムな文字列にする」「前述二つを実施するのはパスワード管理ツール無しでは無理なので、適当なツールを利用する」の三点をしっかりまもろう。

そしてエンジニアもSMS認証は弱いと言う事をちゃんと認識してサービスを設計しよう。

マイナンバーカードへの保険証統合と、旧保健証廃止に思うこと

マイナンバーカードと保険証を統合して、既存の保険証を原則廃止しようという計画がニュースになってる。落としたら不安とか、再発行に時間がかかりすぎるとか、個人情報が:・・とか色々と反対意見が出てるけど、IT側からの話しをしたい。

IT担当者側からすると「保険証を統合して、既存の保険証を原則廃止する」というのは、かなり酷い仕様変更である。保険証とマイナンバーカードに求められる非機能要件がまったく異なるためだ。

マイナンバーカードは行政業務効率化を目的に設計されている。効率化が目的なので、実は無くても困らない。電子証明書としての機能が使えなくなったとしても、旧来の非効率な業務で仕事をするために、公務員の残業が増えるだけだ。関わるのは申請者本人と行政担当者だけなので、多少の問題は当事者間の交渉で解決できる。本人確認のための身分証としての機能や、印刷されているマイナンバーなんておまけに過ぎない。

保険証は医療保険を使用するときの本人認証と、医療保険の決済手続を目的に設計されている。認証できないと十分な医療を受けられず、健康を損ねたり、人命にかかわる場合もあり得る。また認証上右方に誤りがあると、1件でも数万円~数千万円の損失が発生する。そのためオフラインでの作業も誤りなく行えることが求められる。事業規模的にIT専任担当者を常駐できないことが多い。

要件をざっくり書き出すとこんなに違う。

マイナンバーカード

  • 平日日中16時間稼働、それ以外は止めても問題は少ない。(稼働率99%程度でもよい)
  • 接続拠点数、5,000箇所(1718市町村、出張所や税務署など含めても3倍程度と推測)
  • 希望者のみが使用する。使わない方は従来通りの方法で手続き。
  • 使用頻度は多くても年に数回。使用時のみ携行する。
  • 障害時には従来通り書類手続きで処理。

マイナンバーカード&保険証(旧保険証原則廃止)

  • 24時間365日無停止。保守点検のための停止も難しい。(稼働率99.99%程度は必要か?)
  • 接続拠点数、266,000箇所(病院8,000、診療所102,000、歯科91,000、調剤薬局60,000、自治体など)
  • 全国民が使用する。未成年者や障碍者(おもに視覚障碍者、知的障碍者、認知症患者、四肢麻痺)への配慮が必要。
  • 使用頻度は多ければ毎月。旅行時など常に携行する必要がある。
  • 障害時にはオフライン認証の仕組みが必要。

導入時点でも無茶をすると思ったが、併用ならまだ突貫工事でも(ぶっちゃけシステムが止まっても)なんとかなる。しかし旧保険証を排除するとなると、非機能要件の差が重くのしかかってくるはずだ。

やっかいなのは病院側のセキュリティ対応だ。今の日本のセキュリティ関連の法律は、情報漏洩をさせた場合の罰則が非常に弱い。「セキュリティパッチを当てていない。」「ログインパスワードを共有していた。」など初歩的セキュリティ対策を怠っていたとして、それで善管注意義務として処罰されることは希であるし、情報漏洩に限らず法人に対する罰則は緩いものが多い。現マイナンバー法でも故意に漏洩させたのでは無い限り罰則は無い。現行のままでは早晩情報漏洩を起こすであろうし、かといって厳しくすれば殆どの病院が対応出来なくなる可能性が高い。

良くある非難への反証

マイナンバーカードの再発行には2週間~1ヵ月もかかる。紛失したらどうするのか?→実は健康保険証の再発行にも1週間~2週間かかる。健康保険証を紛失した場合には「健康保険被保険者資格証明書」を即日発行して対応している。「健康保険被保険者資格証明書」の有効期間は1週間程度と極めて短い。同様の代替手段を整備する必要は有るだろう。

Stable Diffusion+Windows 10+AMD GPUの環境で動かす

Stable DiffusionをWindows 10とAMD GPU上で動作させている記事、Running Stable Diffusion on Windows with an AMD GPUを見つけたので、実践。以下は作成してみたサンプル。

stable_diffusion_sample

事前準備

まずは実行環境を確認。
・メモリ6GB以上のAMD GPU
・Pythonの3.7、3.8、3.9、3.10がインストールされている
・Gitがインストールされている。
・Hugging Face(Stable Diffusionの学習済みモデルを公開している)のアカウント
・6GBの学習モデルをダウンロード&変換する勇気

と言うわけでPythonのインストール。PythonはMicrosoft StoreからPython 3.10をインストールしてしまうのが手がかからない。

続いてGitのインストール。私はVisual Studioと一緒にインストールされてしまっているが、単独でインストールするならGit for Windowsをインストールするのが良いかと思う。

さらにHugging Faceにユーザー登録する。ユーザー登録自体は無料。

インストール作業

ますはインストール先となるフォルダを作成する。私はD:\stable-diffusionにした。インストールには15GB程度の空き容量が必要になるはずなので、十分に空きのあるドライブを選ぶこと。PowerShellからコマンドプロンプトを開いて、インストール先フォルダに移動します。

Microsoft’s DirectMLに対応したOnnx runtimeをhttps://aiinfra.visualstudio.com/PublicPackages/_artifacts/feed/ORT-Nightlyからonnxruntime-directmlをダウンロードする。ダウンロードするランタイムはPythonのバージョンによって異なる。Python 3.10.xをインストールしているなら、cp310(onnxruntime_directml-1.12.0-cp310-cp310-win_amd64.whl)をダウンロードする。

コマンドプロンプトまたはPowerShellのプロンプトを開いて以下のコマンドを実行。

python -m venv ./virtualenv
./virtualenv/Scripts/Activate.ps1 または virtualenv\Scripts\activate.bat
pip install diffusers==0.3.0
pip install transformers
pip install onnxruntime
pip install protobuf<3.20.x
pip install onnx
pip install pathToYourDownloadedFile/ort_nightly_whatever_version_you_got.whl --force-reinstall

Stable Diffusionの学習済みモデルをダウンロードするには、ライセンス条項に同意する必要があります。ライセンス条項を確認した上で、Hugging Faceのhttps://huggingface.co/CompVis/stable-diffusion-v1-4のページにアクセスして、「 have read the License and agree with its terms」にチェックをして、Access Repositoryをクリック。

Hugging FaceのAccess Tokenを発行する必要があります。Hugging FaceのWEBページ右上のユーザーアイコンから「Settings→Access Tokens」に進み、Access Tokenを発行します。

プロンプトから以下のコマンドを実行します。

huggingface-cli.exe login

Token:とプロンプトが表示されるので、先ほど発行したAccess Tokenを入力します。「Your token has been saved to ~」と表示されれば大丈夫です。

Stable DiffusionをOnnixに変換するためのスクリプトを「https://raw.githubusercontent.com/huggingface/diffusers/main/scripts/convert_stable_diffusion_checkpoint_to_onnx.py」からダウンロードします。

以下のコマンドを実行します。

python convert_stable_diffusion_checkpoint_to_onnx.py --model_path="CompVis/stable-diffusion-v1-4" --output_path="./stable_diffusion_onnx"

学習モデルのダウンロードと変換に数時間かかります。気長に待ってください。

試しに画像を生成してみます。以下の内容をtext2img.pyとして保存して、プロンプトから実行してみてください。output.pngが無事に生成されればインストール成功です。

from diffusers import StableDiffusionOnnxPipeline
pipe = StableDiffusionOnnxPipeline.from_pretrained("./stable_diffusion_onnx", provider="DmlExecutionProvider")

prompt = "A happy celebrating robot on a mountaintop, happy, landscape, dramatic lighting, art by artgerm greg rutkowski alphonse mucha, 4k uhd'"

image = pipe(prompt).images[0] 
image.save("output.png")

生成する画像サイズや、反復計算する回数などのパラメータを指定する事もできます。それらを指定する場合には以下のコードを参考に書き換えてください。

from diffusers import StableDiffusionOnnxPipeline
import numpy as np

def get_latents_from_seed(seed: int, width: int, height:int) -> np.ndarray:
    # 1 is batch size
    latents_shape = (1, 4, height // 8, width // 8)
    # Gotta use numpy instead of torch, because torch's randn() doesn't support DML
    rng = np.random.default_rng(seed)
    image_latents = rng.standard_normal(latents_shape).astype(np.float32)
    return image_latents

pipe = StableDiffusionOnnxPipeline.from_pretrained("./stable_diffusion_onnx", provider="DmlExecutionProvider")
"""
prompt: Union[str, List[str]],
height: Optional[int] = 512,
width: Optional[int] = 512,
num_inference_steps: Optional[int] = 50,
guidance_scale: Optional[float] = 7.5, # This is also sometimes called the CFG value
eta: Optional[float] = 0.0,
latents: Optional[np.ndarray] = None,
output_type: Optional[str] = "pil",
"""

seed = 50033
# Generate our own latents so that we can provide a seed.
latents = get_latents_from_seed(seed, 512, 512)
prompt = "A happy celebrating robot on a mountaintop, happy, landscape, dramatic lighting, art by artgerm greg rutkowski alphonse mucha, 4k uhd"
image = pipe(prompt, num_inference_steps=25, guidance_scale=13, latents=latents).images[0]
image.save("output.png")

制限

この時点ではいくつかの制限があります。

生成画像解像度の制約

512×512以上の解像度の画像を生成することが出来ません。これはOnnixの制約です。より高解像度の画像が必要な場合には、waifu2xなどの超解像AIを併用するなどの工夫が必要です。

NSFW(職場閲覧注意)による制約

真っ黒な画像を生成することが頻繁にあります。標準で組み込まれているNSFW(職場閲覧注意)フィルタにブロックされていることが原因です。以下の様に1行追加する事でNSFWを無効に出来るようですが、Stable Diffusionの利用規約上NSFWを無効にして生成した画像の公開は慎重に行いましょう。NSFWを無効にすると相当に高速になるという副次的効果もあるようです。

pipe = StableDiffusionOnnxPipeline.from_pretrained("./stable_diffusion_onnx", device_map="auto", provider="DmlExecutionProvider", max_memory=max_memory_mapping)
pipe.safety_checker = lambda images, **kwargs: (images, [False] * len(images))

その他、細かなHackは続編のStable Diffusion Updatesに・・・・

いろいろな資格の投資対効果

半分趣味で資格を取るなら投資対効果なんて考える必要は無いが、賃金を増やす為に資格を取ろうと考えて居るなら投資対効果を考えるのは大事だ。中小企業診断士の資格が割に合わないと言う話しをTwitterで見かけたので、ざっくりと計算してみた。

各数字は凄く雑なので、細かい突っ込みは受け付けない。資格取得費用の大部分は勉強にかかる時間によって発生する機会損失である。今回は1時間2,500円で計算している。1年辺りの資格手当の額は求人などをざっくり見て中央付近を採用している。資格取得を切っ掛けに独立起業する事は想定していない。

資格名受験費用その他費用学習時間総費用資格手当維持費用増益投資効率
中小企業診断士302301,0002,760180561244%
公認会計士201503,0007,6701,2001201,08014%
税理士81102,0005,11860010050010%
社会保険労務士15301,0002,5453604032013%
情報処理安全確保支援士8205001,278180661149%
電気工事士二種11301002916006021%
公立大学2,5007,680217,0001,75001,7508%
金額は千円

なるほど、中小企業診断士の投資効率は4%と割りに合わなそうである。同じコストを投じて資格を取るなら、社会保険労務士の方がはるかにマシである。

工事士などのガテン系資格は資格取得による賃金増加額こそ小さいものの、投資効率は極めて高そうだ。工事士系資格で資格手当を貰えるような職場なら、確実に抑えていくのが良さそうだ。

あるていど勉強ができるなら当たり前のように大学進学を選択するが、投資効率は決して高くはない。在学中に勉強していることによる機会損失は意外に大きいのだ。投資効率を重視するなら、工業高校か高専でIT系資格を取得して就職するのが一番良いのかもしれない。