Stable Diffusion環境構築メモ
PCスペック
このPCはノートです。単に動かしてみたいだけだから。
CPU:AMD Ryzen 7 4700U with Radeon Graphics 2.00 GHz
CPUは大して重要ではない。
RAM:8.00 GB (7.37 GB 使用可能)
ギリギリ。
グラフィックボード:

VRAMが4GB。超ギリギリ。まさに最小スペックといった感じ。
Pythonインストール
バージョン3.10.6をインストールする。

PATHに追加するか?のチェックが出なかったので、自力でC:\Users\ユーザー名\AppData\Local\Programs\Python\Python310を環境変数PATHに追加した。
ソースのダウンロード
コミットしないので、git cloneする必要はなし。Zipでダウンロードする。
この環境はCUDAではなくRadeonなのでForkされたこれを使う。
インストール
webui-user.batにPython.exeのパスを追加する。PATHに追加した意味あったかな?

webui-user.batを実行する。

こんな感じで進む。ネットワーク環境及びCPU及びストレージ書き込みの能力に依存した時間を消費する。
すると、ここでランタイムエラー。

結局git cloneはさせられるんかい!
git-scm.com
しゃーなしGitインストール。
もう一回webui-user.batを実行。
完了。
実行してみる

WebUIが表示できました。

ビックリするほどダメ!
Raspberry Pi Zero W セットアップ手順メモ
1.OS(Raspbian)のセットアップ
ほぼここの通り。情報が少々古いので読み替えていく。
1.1.Raspbian Busterのダウンロード
今回はストレージサイズが32GBと余裕があるため、「Raspbian Buster with desktop and recommended software」を選択する。
ZIPを設定用PCに保存。
1.3.イメージファイルのマウント
1.1.でダウンロードしたZIPを解凍すると.imgファイルがある。
これを何でもいいのだが、ここでは情報通りbalenaEtcherでmicroSDカードにマウントした。
www.balena.io
microSDカードをRaspberry Piに挿入する。
2.起動&初期設定
2.1.初回起動
起動する。
と言ってもスイッチはないため、PWR用USB-microBとUSB給電できる何らかの装置を接続すれば起動する。
Welcome to Raspberry Pi Desktop!と言われるのでThank you.と唱えてNext
Set CountryでJapan, Japanese, Tokyoを選択。設置場所が海外ならTimezoneはそこにする。
パスワードは標準ではオートログインになっている。変えたければ変える。もちろん変えた方がセキュリティ的に良い。
ここからはネットワークが必要な作業。
2.2.Wifi接続設定
Zero Wを買ったためWifiはモジュールなしで対応している(802.11 b/g/n)。
しかし元記事のようにすんなりとはWifiに繋がらなかったため、Check for updates以降はSkipで一旦ウィザードを終了させた。
ターミナルを開いて直接configを編集してしまおう。
>sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
と入力してEnterするとWPA設定ファイルを開けるので、
network={
ssid="*****" (繋ぎたいWifiのSSID)
psk="******" (繋ぎたいWifiのpassword)
key_mgmt=WPA-PSK
scan_ssid=1
}として、上書き保存。
そしてメニューからRaspbianのリブートを行う。
すると、再起動後からWifiに接続できた。
2.3.VNC,SSH等の有効化
メニューから「設定」→「Raspberry Piの設定」を開く。
色々ならんでいるのでVNCとSSHを有効にする。
2.4.IPアドレスの固定
IoTを実現するのにあたって、最も重要な設定の1つ。
これも直接configをいじって解決する。
>sudo nano /etc/dhcpcd.conf
としてdhcpcd.confを開き、
interface wlan0 static ip_address=[192.168.XXX.XXX]/24 (設定したい固定IPアドレス) static routers=[192.168.XXX.XXX] (デフォルトゲートウェイのIPアドレス) static domain_name_servers=[192.168.XXX.XXX](DNSサーバーのIPアドレス、大抵デフォルトゲートウェイのIPアドレスと同じ)
として、上書き保存。
そしてメニューからRaspbianのリブートを行う。
3.まとめ
以上で「同一LAN上からアクセスできるシングルボードコンピュータ」の準備が完了した。
ではこれを何に使うか?
それはこれから考えれば良いことだ。
AngleSharpで上州屋の釣果情報をぶっこ抜く2
前回までのあらすじ
my-income-is-small.hatenablog.com
継続的アウトプット!継続的アウトプット!
進捗
- 11件目以降のデータも取得する→できた
- dataGridのサイズを動的に変える→まあできた
- 魚の名前を抽出する→まだ
- 縮小した釣果画像を出す→あえてやらない
- リファクタリング(もうかよ)→少しやった
ソース
public partial class Form1 : Form
{
private int pageNum = 1;
private int numOfArticlesPerPage = 10;
public Form1()
{
InitializeComponent();
}
private async void button1_ClickAsync(object sender, EventArgs e)
{
var urlstring = urlTextBox.Text;
await GetHTMLFromStream(urlstring);
}
/// <summary>
/// 指定したサイトのHTMLをストリームで取得する
/// </summary>
/// <param name="urlstring">URL</param>
/// <returns></returns>
private async Task GetHTMLFromStream(string urlstring)
{
// データ表示の設定
SetDataGridViewProperty();
// 指定したサイトのHTMLをストリームで取得する
var doc = default(IHtmlDocument);
using (var client = new HttpClient())
using (var stream = await client.GetStreamAsync(new Uri(urlstring)))
{
// AngleSharp.Html.Parser.HtmlParserオブジェクトにHTMLをパースさせる
var parser = new HtmlParser();
doc = await parser.ParseDocumentAsync(stream);
}
// HTMLから県を抽出する
ExtractArea(doc);
// HTMLから釣り場と釣り人を抽出する
ExtractPointAndAngler(doc);
// HTMLから店員コメントを抽出する
ExtractComment(doc);
}
/// <summary>
/// DataGridViewの設定
/// </summary>
private void SetDataGridViewProperty()
{
// DataGridViewの列数とヘッダーの設定
chokaDataGridView.ColumnCount = 4;
chokaDataGridView.Columns[0].HeaderText = "prefecture";
chokaDataGridView.Columns[1].HeaderText = "point";
chokaDataGridView.Columns[2].HeaderText = "angler";
chokaDataGridView.Columns[3].HeaderText = "comment";
// DataGridViewの列幅・改行の設定
chokaDataGridView.Columns[3].DefaultCellStyle.WrapMode = DataGridViewTriState.True;
}
/// <summary>
/// info__areaから県を抽出する
/// </summary>
/// <param name="doc">HTMLドキュメント</param>
private void ExtractArea(IHtmlDocument doc)
{
var info_area = doc.GetElementsByClassName("info__area");
int i = 0;
foreach (var element in info_area)
{
string prefecture = string.Empty;
foreach (var p in element.GetElementsByClassName("info__tag"))
{
prefecture += p.TextContent.ToString();
}
chokaDataGridView.Rows.Add(prefecture, string.Empty, string.Empty, string.Empty);
// DataGridViewのコメント列の幅を自動調整する
i++;
}
}
/// <summary>
/// info_detailから釣り場と釣り人を抽出する
/// </summary>
/// <param name="doc">HTMLドキュメント</param>
private void ExtractPointAndAngler(IHtmlDocument doc)
{
var info_details = doc.GetElementsByClassName("info__detail");
int i = 0;
foreach (var element in info_details)
{
string point = string.Empty;
string angler = string.Empty;
foreach (var children in element.GetElementsByTagName("tr"))
{
string label = string.Empty;
foreach (var th in children.GetElementsByTagName("th"))
{
label += th.TextContent.ToString();
}
foreach (var td in children.GetElementsByTagName("td"))
{
if (label == "釣り場")
point += td.TextContent.ToString();
else if (label == "釣り人")
angler += td.TextContent.ToString();
}
}
chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[1].Value = point;
chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[2].Value = angler;
i++;
}
}
/// <summary>
/// info_commentを抽出する
/// </summary>
/// <param name="doc">HTMLドキュメント</param>
private void ExtractComment(IHtmlDocument doc)
{
var info_comments = doc.GetElementsByClassName("info__comment");
int i = 0;
foreach (var element in info_comments)
{
string comments = string.Empty;
foreach (var p in element.GetElementsByTagName("p"))
{
comments += p.TextContent.ToString();
}
chokaDataGridView.Rows[i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage)].Cells[3].Value = comments;
// DataGridViewのコメント列の幅を自動調整する
chokaDataGridView.AutoResizeColumn(3, DataGridViewAutoSizeColumnMode.AllCells);
chokaDataGridView.AutoResizeRow(i + (numOfArticlesPerPage * pageNum - numOfArticlesPerPage), DataGridViewAutoSizeRowMode.AllCells);
i++;
}
}
/// <summary>
/// 次の10件を取得する
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void nextButton_Click(object sender, EventArgs e)
{
pageNum++;
var urlstring = urlTextBox.Text + "/search.php?pref=&page=2";
await GetHTMLFromStream(urlstring);
}
}カラムのNo.をハードコーティングしたりしてて酷いけど次で直すから許して
動作確認
OKボタン押したとき

だいぶ見れるようになってきた。凝りだすとキリがないし、文字情報だけにとどめておく意味もあるからUIはしばらくこれでいいかな。
Nextボタン押したとき

次の10個をちゃんと表示してる
AngleSharpで上州屋の釣果情報をぶっこ抜く
釣り情報は釣具店に集まるのが定番である。
釣具店は数多くあれど、釣り業界のIT化の遅れはスポーツ・レジャー界において屈指であり、店舗に赴くことなく情報を得られるのは上州屋・ポイント・キャスティングの3大釣具チェーンのみであると言ってよい。
3大釣具チェーンの中で最もコンテンツが自分にマッチしてるのは上州屋である。
ポイント
- 情報が釣果持ち込みに偏っており、子供が釣ったとんでもない小型魚など、不要コンテンツが多い
- どう釣ったかの情報が不足している
- 関東の情報が少ない
キャスティング
- 更新頻度が低い
- 釣れていない
しかし、上州屋の釣果情報にも以下の問題がある。
会社でも見たいので、いかにも釣り情報見てる、というような状態を作りたくない
よって、スクレイピングのモチベーションができた。自分の見たい情報を店舗によらず的確に収集したいし、その閲覧はあたかもデータの集約された表を眺めているようにしたい。
htmlを文字列解析して作れるかなと思ったけど、
文字列の解析をするのって嫌だ(人間が読むときにいちいち解析してるわけじゃないのに)
何かXMLって嫌だな
ということで、AngleSharpというものを使ってみることにした。
使い方
qiita.com
使い方も簡単でいいね。
超お試し版の作成
上州屋の釣果情報の「1釣果情報」あたりのhtmlは以下のようになっている。
<!--コンテンツ--> <div class="info__body"> <div class="info__bodyin"> <div class="info__head"> <div class="info__date"> <p class="info__date_txt"><span>‘19 </span>08月05日</p> </div> <div class="info__area"> <span class="info__tag">愛知県</span>豊川店 <span class="info__link"><a href="../shop/top.php?s=136">店舗情報</a></span> <span class="info__link"><a href="../shop/choka.php?s=136">店舗の釣り情報</a></span> </div> <p class="info__note">登録日:2019年08月06日<br>画像をクリックすると、拡大画像が表示されます</p> </div> <div class="info__photo_wrap"> <div class="slider info__photo"> <div class="slider-for" id="slider-for3307238"> <div class="sp-slide"><a href="../choka_img/3307238_1.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_1.jpg" alt=""></a></div> <div class="sp-slide"><a href="../choka_img/3307238_2.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_2.jpg" alt=""></a></div> <div class="sp-slide"><a href="../choka_img/3307238_3.jpg" class="boxer" data-gallery="gallery" title=""><img src="../choka_img/3307238_3.jpg" alt=""></a></div> </div> <div class="slider-nav-wrap"> <div class="slider-nav" id="slider-nav3307238"> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_1.jpg" alt=""></span></div> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_2.jpg" alt=""></span></div> <div class="sp-thumbnail"><span><img src="../choka_img/3307238_3.jpg" alt=""></span></div> </div> <div class="slick-nav-arrows"> <div class="slick-next"></div> <div class="slick-prev"></div> </div> </div> </div> </div> <div class="info__box"> <div class="info__boxin"> <div class="info__boxin02"> <div class="clearfix"> <p class="info__sttl">豊川店(愛知県):2019年08月05日の釣果</p> <p class="info__weather" style="display:">晴れ</p> </div> <table class="info__table"> <tbody> </tbody> </table> <div class="info__detail"> <table style="display:"> <tr> <th>釣り場</th> <td>豊川河口周辺</td> </tr> </table> <table style="float:right; display:"> <tr> <th>釣り人</th> <td>スタッフ 林</td> </tr> </table> </div> </div> <div class="info__comment"> <p>豊川河口周辺のハゼの様子を見てきました。<br /> 現場到着時は下げ潮。<br /> ルアータックルで少し遠投したところにハゼが固まっていました。<br /> のべ竿の方たちは苦戦気味でした。<br /> 日によって群れが溜まる場所が変わります。<br /> リール竿も用意していきましょう。<br /> 詳細は御来店の上スタッフにお問い合わせ下さい。</p> <p class="choka_item"> </p> </div> </div> </div> </div> </div> <!--コンテンツ-->
このように、info_detailクラスの中に釣り人と釣り場、info_commentクラスの中に詳細が記述されているのがわかる。本当はinfo_detailの中にコメントも入れてほしい所だが、そうなってないのが初学者たる私にはちょっと厄介であった。パイロット版を作ったら早速ダサい実装になってしまった。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using AngleSharp.Html.Dom; using AngleSharp.Html.Parser; namespace Chokascraper { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_ClickAsync(object sender, EventArgs e) { var urlstring = textBox1.Text; dataGridView1.ColumnCount = 3; // 指定したサイトのHTMLをストリームで取得する var doc = default(IHtmlDocument); using (var client = new HttpClient()) using (var stream = await client.GetStreamAsync(new Uri(urlstring))) { // AngleSharp.Html.Parser.HtmlParserオブジェクトにHTMLをパースさせる var parser = new HtmlParser(); doc = await parser.ParseDocumentAsync(stream); } // HTMLからtitleタグの値(サイトのタイトルとして表示される部分)を取得する var title = doc.Title; var info_details = doc.GetElementsByClassName("info__detail"); foreach (var element in info_details) { string point = string.Empty; string angler = string.Empty; foreach (var children in element.GetElementsByTagName("tr")) { string label = string.Empty; foreach (var th in children.GetElementsByTagName("th")) { label += th.TextContent.ToString(); } foreach (var td in children.GetElementsByTagName("td")) { if (label == "釣り場") point += td.TextContent.ToString(); else if (label == "釣り人") angler += td.TextContent.ToString(); } } dataGridView1.Rows.Add(point, angler, ""); } var info_comments = doc.GetElementsByClassName("info__comment"); int i = 0; foreach (var element in info_comments) { string comments = string.Empty; foreach (var p in element.GetElementsByTagName("p")) { comments += p.TextContent.ToString(); } dataGridView1.Rows[i].Cells[2].Value = comments; i++; } } } }
結果

見た目はカスだが、とりあえず先頭10件の釣り人・釣り場・コメントの情報を得ることは出来ているみたい。
FFMpegをキックして簡単な動画変換ソフトを作る1
昨日のエントリーとも関係あるのですが,俺は動画変換をするときにWindowsムービーメーカーなんて使わず,ffmpegを使ってコマンドラインから色々オプションをイジって変換しています。しかしこの作業がだるい。
ffmpeg -i inputfile.flv -f mp4 -vcodec libx264 -acodec libfaac -vb 512k -ab 192k outputfile.mp4
とか手で打つのがムズい。途中で間違ってエンター押しちゃう。だからVisualStudioを使って,ffmpegを好みのオプションでキックするだけという簡単なプログラムを作ってしまおう
せっかくなので仕様をちゃんとさせておこうと思います
仕様
【機能要件】
扱うデータの種類:ビデオファイル一般
処理内容:ffmpegをGUIから選択したオプションで起動し,入力データを変換
画面表示:ウインドウ1つ
操作の方法:ファイルを開いてコンボボックスからオプション選んでボタン押すだけ
出力の形式:ビデオファイル(コーデックがあるもの)
【非機能要件】
機能性:動けばいいよ
信頼性:PCが爆破しなければよし
使用性:俺が使えればよし
効率性:暇な時しか使わないので何でもよし
保守性:保守しないのでOK
移植性:ここでしか使わないのでOK
障害抑制性:障害発生したら消せばOK
効果性:投資してないし何でも
運用性:使うの俺だけだからどうでもいいい
技術要件:ウイルス作りに定評のあるVisual Studio
【入力】
■ソースファイル
任意の動画ファイルとする。フォーマットは問わない。(OpenFileダイアログ)
■オプション
・アスペクト
4:3と16:9からの選択とする。(コンボボックス)
・サイズ
アスペクトが4:3の場合QVGA・VGA・SVGA・XGA・Quad-VGA・SXGA+から,
16:9の場合はWQVGA+・HVGAW・WVGA・WSVGA・HD 720p・HDから選択する。(コンボボックス)
・フォーマット
ビデオコーデックとオーディオコーデックを選択する。(コンボボックス)
・ビットレート
同じくビットレートを選択する。ビデオのフレームレートは固定でOK(コンボボックス)
・タイトル
出力ファイル名を入力する。(テキストボックス)
【出力】
■アウトプットファイル
変換された動画ファイル
【その他】
面倒くさいからシングルスレッドのみ実装する。CPUがんばれ。コア数に勝手に最適化されるような気がしたから別にいいのかもしれない
はてなブログにきれいにソースコードを表示させる
はてなブログによるアウトプットの基本中の基本。
元々ソースコード記述に強いことからはてなダイアリーは技術者に愛されてきた。
はてなブログでも当然その流れは受け継いでおり、きれいに表示させることが容易にできる。
はてな記法を選択する
編集の▽より「はてな記法」を選択する。

これでソースコードをきれいに表現することができる。実は見たまま記法でもプレビューをどうにかして行うことができるようだが、多分作業時間的にはてな記法のほうが早い。Markdown記法でも同じようにできるが、はてなに慣れた状態ならはてな記法が最もすんなり入れるだろう。
「 スーパーpre記法(シンタックス・ハイライト)」でソースコードを記述する。
上記ヘルプにあるとおりに、「>|??| 」「||<」で囲むことにより、??(言語)で使用される構文規則をハイライト表示できる。例えば、

上記のように記述すると(編集画面の時点でインデントが美しく再現されてとても良い)、
using System; namespace Test { class HelloWorld { static void Main() { Console.WriteLine("Hello World!"); // ウィンドウを出し続けるため Console.WriteLine("終了するには何かキーを押してください"); Console.ReadKey(); } } }
このように、美しく表示される。これで継続的アウトプットの準備が完了したも同然。準備倒れにならないようにしないと。

