Mac って何?それって美味しいの?

マイコンと呼ばれた時代から現在まで、使用記から何から何でも。

古い世代のIntel MacにStable diffusion WebUI AUTOMATIC1111 / Forgeをインストールする方法


2011年以降のMacに画像生成AIのStable diffusion WebUI AUTOMATIC1111 と Forgeをインストールする方法を紹介します。

今どきのマシンから見たら生成時間は比較にならない遅さですが、それでも遊びなら許される程度まで頑張りました。

想定スペックは、CPUがCore i5 または i7 、 RAMが 8GB以上です。

OSはCatalina以降でないとPythonインストール時にエラーが発生する恐れがあります。

OCLP等で最近のOSをインストールされることをお勧めします。

生成時間が短いForgeの使用をお勧めしますが、継続した開発には不安があるため、ストレージに余裕があるなら両方インストールしてもいいかと思います。

 

AUTOMATIC1111インストール

1.ターミナルウィンドウを開きます。

2.Homebrew がインストールされていない場合は、 https://brew.shの手順に従ってインストールしてください。Homebrewインストール終了時に表示される「Next steps」の指示に従って Homebrew を PATH に追加します。

3.ターミナルウィンドウでPythonインストールを実行します。

brew install cmake protobuf rust python@3.10 git wget

4.次のコマンドを実行して、Web UI リポジトリのクローンを作成します。

cd ~
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui

5.使用するStable Diffusion モデル/チェックポイントをstable-diffusion-webui/models/Stable-diffusionに配置します。古いマシンでは画像サイズが大きくなると生成時間を多く必要としますので、SD1.5ベースのモデルを推奨します。

6.古い世代のIntel MacではGPUを使えませんのでOFFにします。

(ユーザ)/stable-diffusion-webui/webui-macos-env.shをエディタで開きます。 

COMMANDLINE_ARGSの設定を以下のように変更します。

export COMMANDLINE_ARGS="--skip-torch-cuda-test --no-half --use-cpu all"

7.Web UIの起動

cd ~/stable-diffusion-webui
./webui.sh

を実行します。 Python 仮想環境は venv を使用して作成およびアクティブ化され、残りの不足している依存関係は自動的にダウンロードされてインストールされます。

終了するときはターミナルウィンドウで ctrl + cキーを押下します。

Web UI を再起動するには、./webui.shを再度実行します。またWeb UIを自動更新するには、git pullをWeb UI起動前に実行します。

cd ~/stable-diffusion-webui
git pull
./webui.sh

 

SD Forgeインストール

すでにAUTOMATIC1111をインストール済みなら手順3まで省略し、手順4から始めてください。

1.ターミナルウィンドウを開きます。

2.Homebrew がインストールされていない場合は、 https://brew.shの手順に従ってインストールしてください。Homebrewインストール終了時に表示される「Next steps」の指示に従って Homebrew を PATH に追加します。

3.ターミナルウィンドウでPythonインストールを実行します。

brew install cmake protobuf rust python@3.10 git wget

4.次のコマンドを実行して、Web UI リポジトリのクローンを作成します。

cd ~
git clone https://github.com/lllyasviel/stable-diffusion-webui-forge

5.使用するStable Diffusion モデル/チェックポイントをstable-diffusion-webui/models/Stable-diffusion-forgeに配置します。古いマシンでは画像サイズが大きくなると生成時間を多く必要としますので、SD1.5ベースのモデルを推奨します。

なお、すでにAUTOMATIC1111で使用のモデル等は、環境を共用する指定をすることでForgeでも使用できます。指定方法は後述

6.Web UIの起動

Python 仮想環境は venv を使用して作成およびアクティブ化され、残りの不足している依存関係は自動的にダウンロードされてインストールされます。

--always-cpu オプションで実行します。

cd ~/stable-diffusion-webui-forge
./webui.sh --always-cpu

AUTOMATIC1111環境を共用する場合は--forge-ref-a1111-homeオプション後にディレクトリ指定します。

cd ~/stable-diffusion-webui-forge
./webui.sh --always-cpu --forge-ref-a1111-home /Users/(ユーザ)/stable-diffusion-webui

ディレクトリの記述は環境に合わせて変更してください。

終了するときはターミナルウィンドウで ctrl + cキーを押下します。

Web UI を再起動するには、./webui.shを再度実行します。またWeb UIを自動更新するには、git pullをWeb UI起動前に実行します。

cd ~/stable-diffusion-webui-forge
git pull
./webui.sh --always-cpu --forge-ref-a1111-home /Users/(ユーザ)/stable-diffusion-webui

 

起動用実行ファイルの作成

ターミナルを開いてコマンドを入力して起動する手順をスクリプトにして、アイコンをダブルクリックで簡単に起動できるようにします。

1.テキストエディタシェルスクリプトを作成し、ファイル名(例): SDForge.command で保存します。

#!/bin/sh
cd ~/stable-diffusion-webui-forge
git pull
./webui.sh --always-cpu --forge-ref-a1111-home /Users/(ユーザ)/stable-diffusion-webui

2.実行可能にパーミッションを変更します。

chmod u+x SDForge.command

3.SDForge.commandをダブルクリックで実行します。また、エイリアスを作成しデスクトップ等に配置できます。

 

LCM LoRAの使用

SD1.5ベースモデルの生成でLCM LoRAを使い、ステップ数を削減して生成時間を大幅に短縮します。

1.latent-consistency/lcm-lora-sdv1-5 at main から pytorch_lora_weights.safetensors をダウンロードします。

2.stable-diffusion-webui(-forge)/models/Loraに配置します。

3.生成プロンプトに追加します。

Loraタブを開き、LCM LoRAをクリックでプロンプトに追加できます。

4.生成パラメータを変更します。

  • Sampling method : LCM、Eulerなど。Euler が安定感あり生成もいくらか速い
  • Sampling steps : 2 から 8、イラスト系は5、実写系は8おすすめ
  • CFG Scale : 1.0 から 2.0 の間、おすすめは1.0
  • LCM_LoRA_Weights : 1、実写系でサンプラーEulerは 0.7 あたりがおすすめ

実行結果

iMac 2011 21.5inch Core i7 RAM16GB、SD Forge、LCM LoRA使用

イラスト系

detailed, woman, cosy cafe <lora:LCM_LoRA_Weights_SD15:1>
Steps: 5, Sampler: Euler, CFG scale: 1, Seed: 2522311664, Size: 512x512, Model hash: 48757d16e7, Model: sakuramix_v70, Lora hashes: "LCM_LoRA_Weights_SD15: aaebf6360f7d", Version: f0.0.17v1.8.0rc-latest-276-g29be1da7
Time taken: 1 min. 44.3 sec.

 

実写系

detailed, woman, cosy cafe <lora:LCM_LoRA_Weights_SD15:0.7>
Steps: 8, Sampler: Euler, CFG scale: 1, Seed: 3925495437, Size: 512x512, Model hash: 48c4cec131, Model: chilled_remix_v2, Lora hashes: "LCM_LoRA_Weights_SD15: aaebf6360f7d", Version: f0.0.17v1.8.0rc-latest-276-g29be1da7
Time taken: 1 min. 58.5 sec.

Windows10上で西暦和暦変換をサクッとできるようにする

ちょっと業務で西暦の日付を和暦に変換しなきゃならなくなりました。
まぁ、Excelとか何か使ってやってもいいのでしょうが、
何か、手間をかけずにできないかな?とchatGPTさんにお願いしたところ
PowerShellスクリプトで実現してくれました!
そんな発想無かったよ。ありがとう!

PowerShellを使用して西暦から和暦への変換と年齢計算を行うスクリプト

はじめに

PowerShellを使って、西暦の生年月日を入力し、その日付を和暦に変換して表示し、さらに現在の年齢も計算して表示する便利なスクリプトを作成しました。この記事では、そのスクリプトの使い方や、背後にあるロジックについて説明します。

スクリプトの概要

このスクリプトは、PowerShellを使って西暦の生年月日を入力し、それを和暦に変換して表示します。また、その人の現在の年齢も計算して表示します。

スクリプト

# 西暦から和暦への変換関数
function ConvertToJapaneseEra {
    param(
        [datetime]$Date
    )

    # 元号の開始日と名称の定義
    $EraStartDates = @{
        [datetime]"1868-01-25" = "明治"
        [datetime]"1912-07-30" = "大正"
        [datetime]"1926-12-25" = "昭和"
        [datetime]"1989-01-08" = "平成"
        [datetime]"2019-05-01" = "令和"
    }

    # 元号の開始日を基準にして和暦を計算
    foreach ($StartDate in $EraStartDates.Keys | Sort-Object -Descending) {
        if ($Date -ge $StartDate) {
            $EraName = $EraStartDates[$StartDate]
            $JapaneseYear = $Date.Year - $StartDate.Year + 1
            $JapaneseDate = "$EraName$JapaneseYear 年 $($Date.Month) 月 $($Date.Day) 日"
            return $JapaneseDate
        }
    }

    return "Invalid Date"
}

# 西暦の生年月日を入力
do {
    $InputDate = Read-Host "西暦の生年月日を入力してください (yyyyMMdd形式、例: 19900101)"

    if ([string]::IsNullOrWhiteSpace($InputDate)) {
        Write-Host "スクリプトを終了します。"
        exit
    }

    try {
        $DateTime = [datetime]::ParseExact($InputDate, "yyyyMMdd", $null)
        $JapaneseDate = ConvertToJapaneseEra -Date $DateTime
        $Age = (Get-Date).Year - $DateTime.Year
        if ((Get-Date) -lt $DateTime.AddYears($Age)) {
            $Age--
        }
        Write-Host "和暦の生年月日は: $JapaneseDate"
        Write-Host "現在の年齢は: $Age 歳"
    }
    catch {
        Write-Host "無効な形式です。正しい形式で入力してください (yyyyMMdd形式、例: 19900101)"
    }
} while ($true)

 

PowerShellスクリプトを実行するには、スクリプトを保存し、.ps1の拡張子でファイルを作成し、PowerShellで実行します。

ただし、スクリプトの実行には、PowerShellの実行ポリシーを緩和する必要があるかもしれません。これを行うには、PowerShellを管理者権限で開き、以下のコマンドを実行します。

Set-ExecutionPolicy RemoteSigned

 

使い方

このスクリプトをダブルクリックで実行できるようにすることができます。以下の手順に従ってください。
1. テキストエディターで、先ほどのスクリプトをコピーします。
2. コピーしたスクリプトを新しいテキストファイルに貼り付けます。
3. テキストファイルを保存しますが、拡張子を `.ps1` に変更します。例えば、`ConvertToJapaneseEra.ps1` という名前にします。
4. 保存した `.ps1` ファイルを任意の場所に配置します。デスクトップに配置するのが便利です。
5. 保存した `.ps1` ファイルに対して、右クリックし、"プロパティ" を選択します。
6. "プロパティ" ウィンドウが開いたら、「一般」タブに移動します。
7. 「種類」セクションの「変更」ボタンをクリックします。
8. 「選択プログラム」ウィンドウが開かれるので、そこで "Windows PowerShell" を選択し、「OK」をクリックします。
9. 再び "プロパティ" ウィンドウに戻り、「適用」または「OK」をクリックします。

実行
1. ダブルクリックでスクリプトを実行します。
2. 指示に従って西暦の生年月日を入力します。何も入力せずにEnterキーを押すと終了します。
3. スクリプトが和暦の生年月日と年齢を表示します。

スクリプトの詳細

このスクリプトは、西暦の生年月日を "yyyyMMdd" 形式で受け入れます。入力された日付を和暦に変換し、その後、現在の日付との差を計算して年齢を求めます。その際、生年月日が現在の日付より後の場合は、年齢を1つ下げます。

このスクリプトを使えば、簡単に西暦から和暦への変換と年齢計算を行うことができます。便利なPowerShellスクリプトとして、ぜひお役立てください。

HDD2基Mac mini ServerにRAID構成の起動ディスクを構築する

Mac Miniに2つの内蔵HDDを使用してRAID構成の起動ディスクを作成する方法は、以下の手順に従って行うことができます。RAID(Redundant Array of Independent Disks)は、複数のディスクを組み合わせて1つの論理的なディスクとして使用するテクノロジーです。RAIDを構築する前に、重要なデータはバックアップすることをお勧めします。

 

 

macOSのインストールUSBを作成

除外されたバージョンのmacOSをインストールするなら、

OpenCore Legacy Patcher(OCLP)

ツールを使用して作成できます。その場合はOCLPの手順に従ってください。

インストール要件を満たしているmacOSのインストールUSBを作成する手順は以下の通りです。

**前提条件**:

- 使用するMacApp StoreアカウントでmacOSのダウンロードが可能であること。
- USBフラッシュドライブ(16GB以上推奨)が利用可能であること。

手順:

1. **App StoreからmacOSをダウンロード**:

   - Macを起動し、App Storeから最新のmacOSインストーラをダウンロードします。通常、インストーラは「Applications」フォルダに保存されますが、自動的に起動する場合がありますので、ターミナルやSpotlightで検索しても良いでしょう。

2. **USBフラッシュドライブの準備**:

   - USBフラッシュドライブをMacに接続します。データがある場合はバックアップを取得してください。
   - Disk Utility(ディスクユーティリティ)を開き、USBドライブを選択します。
   - "消去"(Erase)をクリックし、適切なフォーマットとスキーム(Scheme)を選択します。例えば、"Mac OS Extended (Journaled)"と"GUID Partition Map"を選択します。その後、ドライブを"消去"します。

3. **Terminalを使用してUSBにインストーラをコピー**:

   - インストール用のUSBドライブが"Untitled"などの名前でマウントされることを確認します。
   - Terminal(ターミナル)を開きます。
   - 以下のコマンドを使用して、ダウンロードしたmacOSインストーラをUSBにコピーします。コマンド中の「MyVolume」はUSBドライブの名前に置き換えてください。

```bash
sudo /Applications/Install\ macOS\ [Version].app/Contents/Resources/createinstallmedia --volume /Volumes/MyVolume
```

   - コマンドを実行すると、管理者パスワードを求められるので入力してください。その後、USBにmacOSのインストール用ファイルがコピーされます。プロセスには数分から数十分かかることがあります。

4. **インストールUSBの使用**:

   - USBフラッシュドライブにmacOSのインストールファイルが正常にコピーされたら、Macを再起動します。
   - Optionキー(またはAltキー)を押しながら電源を入れておくと、起動時にブートオプションが表示されます。そこで、USBドライブを選択して起動します。

以上の手順で、USBフラッシュドライブにmacOSのインストールファイルを作成し、それを使用してMacを起動することができます。

 

macOSクリーンインストール

すでにあるmacOS起動ドライブをRAIDに復元する場合は、この手順を省略できます。

macOSクリーンインストールする手順は以下の通りです。クリーンインストールを行う前に、重要なデータのバックアップを取得することを強くお勧めします。

**手順:**

1. **バックアップ**:

   - クリーンインストールを行う前に、重要なファイルやデータをバックアップします。Time Machineなどのバックアップツールを使用して、全体のバックアップを取得することをお勧めします。

2. **Macのシャットダウン**:

   - Macをシャットダウンします。

3. **インストールUSBを挿入**:

   - 作成したmacOSのインストールUSBドライブをMacに挿入します。

4. **起動オプションを選択**:

   - Macを起動し、Optionキー(またはAltキー)を押しながら電源を入れると、起動オプションが表示されます。USBドライブを選択して起動します。

5. **macOSインストール**:

   - USBドライブから起動したら、macOSのインストールウィンドウが表示されます。
   - 上部のメニューバーから「ディスクユーティリティ」を選択し、クリーンインストールするためにインストール先のドライブを準備します。
   - ディスクユーティリティで、インストール対象のドライブを選択し、"消去"(Erase)をクリックします。適切なフォーマットとスキーム(Scheme)を選択してドライブを消去します(通常、"Mac OS Extended (Journaled)"と"GUID Partition Map"を選択します)。
   - 消去が完了したら、ディスクユーティリティを閉じて、macOSのインストールウィンドウに戻ります。
   - "続ける"をクリックして、先ほど消去したドライブにmacOSをインストールします。

6. **インストールプロセス**:

   - インストールが開始されると、Macに最新のmacOSがインストールされます。インストールには数分から数時間かかることがあります。

7. **初期設定**:

   - インストールが完了すると、Macが再起動されます。最初の起動時には初期設定を行います。言語、地域、Apple ID、ネットワークなどの情報を入力して設定を完了させます。

8. **データの復元**:

   - バックアップから必要なデータやファイルを復元します。Time Machineなどのバックアップを使用すると、復元プロセスがスムーズに行えます。

これらの手順に従うことで、クリーンな状態で最新のmacOSがインストールされたMacを使用することができます。

 

macOS起動ドライブを外部ディスクにコピー

macOSの起動ディスクをUSBストレージに復元する手順は以下の通りです。このプロセスでは、元の起動ディスク(たとえば、Macの内蔵ストレージ)からUSBにmacOSをコピーして復元します。

**手順:**

1. **Macのシャットダウン**:

   - Macをシャットダウンします。

2. **インストールUSBを挿入**:

   - 内蔵ストレージではなく外部ストレージから起動させるため、macOSのインストールUSBドライブをMacに挿入します。

3. **起動オプションを選択**:

   - Macを起動し、Optionキー(またはAltキー)を押しながら電源を入れると、起動オプションが表示されます。USBドライブを選択して起動します。

4. **macOSインストールユーティリティ起動**:

   - USBドライブから起動したら、macOSのユーティリティウィンドウが表示されます。

USBインストーラから起動したmacOSユーティリティ

5. **退避用USBストレージの接続**:

   - バックアップや重要なデータを失わないように、空のUSBフラッシュドライブ(16GB以上推奨)をMacに接続します。データがある場合は、バックアップを取得してください。

6. **Disk Utilityを使用してUSBを準備**:

   - Disk Utilityを開きます。
   - 退避用USBドライブを選択し、「消去」をクリックして、適切なフォーマットとスキーム(Scheme)を選択します。通常、"Mac OS Extended (Journaled)"と"GUID Partition Map"を選択します。
   - "消去"をクリックして退避用USBドライブを準備します。このプロセスでUSBディスク上のすべてのデータが消去されることに注意してください。

7. **Disk Utilityで内蔵ストレージのmacOSをUSBに復元(コピー)**:

   - Disk Utilityを開きます。
   - 左側のメニューで、復元(コピー)先ディスクを選択します。
   - 上部のメニューバーで「復元」を選択します。
   - 「ソース」にmacOSが入った内蔵ストレージを選択します。
   - "復元"をクリックしてUSBにmacOSを復元します。このプロセスには時間がかかる場合があります。

8. **復元が完了したらUSBを確認**:

   - 復元が完了したら、USBドライブが起動可能なmacOSを持つディスクとして正常に作成されたかどうかを確認してください。

これらの手順に従って、macOSの起動ディスクをUSBストレージに復元(コピー)することができます。

 

RAID構成の起動ドライブを構築し退避用USBストレージから復元

引き続きUSBインストーラで起動した状態で作業します。

1. **Disk Utilityを使用してRAID構築**:

   - Disk Utilityを開きます。
   - 左側のメニューで、"RAID Assistant"または"RAID"タブを選択します。
   - "Create a RAID Set"をクリックします。
   - RAID構成のタイプ(RAID 0RAID 1、または他のRAIDレベル)を選択します。RAID 0はストレージを結合し、速度を向上させますが、耐障害性が低くなります。RAID 1はデータをミラーリングし、耐障害性を高めます。
   - 使用するディスクを選択して、RAID構成に追加します。
   - RAID構成名やフォーマットなどの設定を行います。
   - "Create"をクリックしてRAID構成を作成します。

2. **起動ディスクの作成**:

   - Disk Utilityを使用して、新しく作成したRAID構成をMac Miniの起動ディスクとして設定します。
   - "View"メニューから"Show All Devices"を選択し、RAID構成を表示します。
   - RAID構成を選択し、"Erase"をクリックします。必要に応じて、フォーマットと名前を設定します。
   - "Scheme"は"GUID Partition Map"を選択し、"Format"は"APFS"または"Mac OS Extended (Journaled)"を選択します。
   - "Erase"をクリックしてフォーマットを完了します。

3. **Disk Utilityで退避用USBストレージからRAID起動ディスクに復元(コピー)**:

   - Disk Utilityを開きます。
   - 左側のメニューで、RAID起動ディスクを選択します。(下の写真の例ではMacHDを選択)
   - 上部のメニューバーで「復元」を選択します。
   - 「ソース」にmacOSが入った退避用USBストレージを選択します。
   - "復元"をクリックしてRAID起動ディスクにmacOSを復元します。このプロセスには時間がかかる場合があります。
   - "復元"の最終段階?でブレスツールエラーが表示されますが無視します。(復元はされているようです)

復元

4. **起動ディスクとして設定**:

   - Startup Diskを開きます。
   - RAID構成が表示されている場合、それを選択してMac Miniの起動ディスクとして設定します。この時、ブレスツールエラーが表示されますが無視して選択します。

起動ディスクを指定してシステム終了

5. **RAID起動ディスクからのmacOS起動を確認**:

   - RAID起動ディスクから復元されたmacOSが正常起動するのを確認してください。

 

以上の手順で、Mac Miniに2つの内蔵HDDを使用してRAID構成の起動ディスクを構築できます。ただし、RAIDを構築する前にデータのバックアップを取得し、慎重に作業を行うことが重要です。また、Mac MiniがサポートするRAID構成の種類やオプションについて、事前に調査することをお勧めします。

Re: らーめんチャレンジ

だめだ。画像生成AI、楽しすぎる。

前回に引き続き「ゆるふわギャル」でラーメンチャレンジ。

 

yummac.hatenablog.jp

Stable Diffusionでチャレンジしてみる。

使用マシンは iMac 2011 core i7 なので気長に待ちます。

ゆるふわギャル eating ramen

chilled_remix_V2, step:25, upscale Latent 2x

これは生成に指定しているモデルにも影響されるよね。

箸を持つ指も苦手なのがわかります。これでも頑張っているほう。

ラーメンをすするゆるふわギャル(学生)

もう、箸を持たずにすすっています。ゆび多め。

ラーメンを食べない

オシャレな感じだけど、もはや食べない。映えればいいの。

 

Bing Image Creatorでも、プロンプトに本気出してみる。

ラーメンを食べるゆるふわギャル(ノスタルジック)

ラーメンを食べるゆるふわギャル(誤生成)

 

プロンプトのちょっとの加減でネコになります。

それにしても、箸を持つ手(指)が上手に描けています。前足か。

 

それから、パステルなゆるふわにしてみる。

ラーメンを食べるゆるふわギャル(ぱすてる)

こちらはまた、ちょっとした加減で「安全でない画像コンテンツが検出されました」扱いでブロックされてしまいます。

ラーメンを食べるゆるふわギャルは「不適切」なのか。

いやいや、「性的」なものを思わせるプロンプトがまずいんだろうね。

らーめんチャレンジ

今週のお題「こぼしたもの」

 

最近、Stable DiffusionをiMacで使って遊んでいるが、なんと、Microsoft Edgeで画像生成できるという記事を見た。

DALL-E3を無料で使え、Stable Diffusionよりも上手く描けるという?!

そして、さっそく試してみた。

Gooole Chrome使いの私なので、ときどき勝手に起動するEdgeは嫌いなヤツだったが、ひさしぶりにダブルクリックした。

Microsoft Bing の Image Creator」のページを表示する。

Edge関係なかった。chromeでもできるやん。

そして、最近お気に入りの「ゆるふわギャル」でラーメンチャレンジ。

プロンプトは日本語でOK。生成したい画像の説明を文章で入力すればいい。

「ラーメンを食べているゆるふわギャル」

ラーメンを食べているゆるふわギャル

かわいいイラストが生成された。

イラストではなくリアルにするには「写真」を指示するのか。

3画像生成された。

ラーメンを食べている「ゆるふわ美少女」の写真

なぜか、ネコが生成されるんだよねぇ。

イラストの時もネコが最初だった。

「ゆるふわ」がふさふさなネコを想像させる?

でもすごいな!楽しい。

 

追加)

Bing AIチャットを使うと勝手に画像生成してくれる!

ただ、AIでプロンプト変換してから画像生成するので期待しているのと出来が異なることが多い。

「ゆるふわギャル」の特徴をチャットでAI自身が説明しているにも関わらず、ネコイラストが生成される。

やっぱりImage Creatorのページでプロンプトに記述したほうが期待していいる画像に近づけると思います。

「写真、ラーメンを食べている「ゆるふわギャル」、髪型:長くて、ウェーブがかかっていることが多いです。 メイク:ナチュラルメイクが多く、ピンクやベージュなどの淡い色を使います。 ファッション:ゆるいシルエットの服を好みます。」

写真、ラーメンを食べている「ゆるふわギャル」、髪型:長くて、ウェーブがかかっていることが多いです。 メイク:ナチュラルメイクが多く、ピンクやベージュなどの淡い色を使います。 ファッション:ゆるいシルエットの服を好みます。

 

iMac 2011 i7 で AUTOMATIC1111 / stable-diffusion-webui が動作するなんて!

google Colab を利用させていただき(無料プランなので低姿勢で)

Stable Diffusionによる画像生成を楽しんでましたが、いよいよ

有料でなければ使えなくなってしまいました。

あの、楽しい画像生成をどうにか無料でできないか。

このiMacでは動かないの?

えっ?動くの?!10年以上前のGPUだよ?

生成時間は目を瞑れる。寝て待つのは苦じゃ無い。

それじゃ、まあ、動くかどうかやってみよー

 

マシン

iMac Core i7 2.8 21.5-Inch mid-2011

CPU : Intel Core I7 2600S Quad Core 2.8 GHz 

GPU : AMD Radeon HD 6770M 512MB

RAM : 16GB

 

インストール

こちらのサイトを参考にさせていただきました。ありがとうございます。

M1/Intel MacにStable DiffusinをインストールAUTOMATIC1111編 | Gift by Gifted

 

私が引っかかったところだけ記述します。

Pythonのバージョンは3.10推奨、3.11はエラーで起動しない

Pythonバージョンは既にインストールしてある3.11の方が新しいし、このままでいいだろうと思い実行したが起動途中エラーで止まってしまう。

3.10をインストールしたら起動できた。

GPUが半精度浮動小数点数計算できないエラー

Generateボタンをクリックするとエラーが出る。

RuntimeError: "LayerNormKernelImpl" not implemented for 'Half'

グラフィックカードが半精度浮動小数点数の計算に対応していないからのようで、

webui-macos-env.shのCOMMANDLINE_ARGSを変更した。

export COMMANDLINE_ARGS="--skip-torch-cuda-test --upcast-sampling --no-half-vae --use-cpu all --precision full --no-half"

GPUは使わずCPUで演算するようになる?

 

そして生成してみた!

stable diffusion on iMac

”cat” step:10 512x512で所要時間は3分30秒くらい

”girl” step:20 512x512で所要時間は7分くらい

step10だとナニコレ画像になるかと思ったけど、悪く無い。普通にネコだ。

step20でおおよそ倍の時間かかったけど、7分くらい全然待てるよ。

プロンプトのウェイトや拡張機能で長くなるだろうけど、

google Colabで、エラーで止まるのを恐れながら使うのを考えたら

使い慣れたiMacで安心してできちゃうのが素敵。

これからも遊ばせていただきます。

 

追記

ImportError: dlopen(/Users/mac/stable-diffusion-webui/venv/lib/python3.10/site-packages/cv2/cv2.abi3.so, 2)

iMac 2012 Core i5 macOS Catalina 10.15.7 で同様のことをした時、上記のエラーが出ました。

対処方法はopencv_python バージョンを 4.7.0.68 (現在このリポジトリで使用されている) から 4.5.5.64 にダウングレードします。

  1. venv をアクティブにします: source ./venv/bin/activate
  2. pip uninstall opencv_pythonを実行すると、 opencv_pythonとcv2の両方をアンインストールすることを通知されるので、 [はい] を選択します
  3. pip install opencv_python==4.5.5.64

Python知識ゼロから始めるchatGPTを利用した超簡単ゲームプログラミング(応用編)

 

yummac.hatenablog.jp

 

前回作成?生成?した、CUIで遊べるカードゲームをGUI画面で遊べるようにする。

Pythonの知識がない私が頼るのはchatGPT様だ。

とりあえずわがまま言ってみる。

「このゲームをGUI画面で遊べるようにコードを作成してください」

回答:

以下は、Tkinterを使用して作成したカードバトルゲームのGUIの一部です。このコードを基にして、GUI画面で遊べるカードバトルゲームを構築することができます。

import tkinter as tk
import random

# カードデッキを作成
suits = ['ハート', 'ダイヤ', 'スペード', 'クラブ']
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'ジャック', 'クイーン', 'キング', 'エース']
deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]

# プレイヤーを作成
player1 = {'名前': 'プレイヤー1', '手札': []}
player2 = {'名前': 'コンピュータ', '手札': []}

# ゲームを初期化
def initialize_game():
    player1['手札'] = []
    player2['手札'] = []
    for _ in range(5):
        player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
        player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
    update_display()

# ゲームの進行
def play_round():
    player1_card = player1['手札'].pop(random.randint(0, len(player1['手札']) - 1))
    player2_card = player2['手札'].pop(random.randint(0, len(player2['手札']) - 1))
    winner = battle(player1_card, player2_card)
    if winner == player1:
        result_label.config(text=f"{player1['名前']}の勝利!")
    elif winner == player2:
        result_label.config(text=f"{player2['名前']}の勝利!")
    else:
        result_label.config(text="引き分け")
    update_display()

# カードのバトル
def battle(player1_card, player2_card):
    rank_order = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'ジャック', 'クイーン', 'キング', 'エース']
    if rank_order.index(player1_card['ランク']) > rank_order.index(player2_card['ランク']):
        return player1
    elif rank_order.index(player1_card['ランク']) < rank_order.index(player2_card['ランク']):
        return player2
    else:
        return None

# 画面の更新
def update_display():
    player1_label.config(text=f"{player1['名前']}の手札: {', '.join([card['ランク'] for card in player1['手札']])}")
    player2_label.config(text=f"{player2['名前']}の手札: {', '.join([card['ランク'] for card in player2['手札']])}")

# Tkinterウィンドウを作成
root = tk.Tk()
root.title("カードバトルゲーム")

# ボタンとラベルを配置
start_button = tk.Button(root, text="ゲームを始める", command=initialize_game)
start_button.pack()
play_button = tk.Button(root, text="ラウンドをプレイ", command=play_round)
play_button.pack()
result_label = tk.Label(root, text="")
result_label.pack()
player1_label = tk.Label(root, text="")
player1_label.pack()
player2_label = tk.Label(root, text="")
player2_label.pack()

# ゲームを初期化
initialize_game()

# Tkinterウィンドウを表示
root.mainloop()

こりゃ楽ちんだぜ!と喜んだのも束の間、実行してみるとコレジャナイ感が。

でも、これをベースにカスタマイズして作り上げるってことだね。

どんどんchatGPTに要望や質問を投げてコードをカスタマイズしていく。

「手札のカードを横に並べて表示し、その中からプレイヤーが選んでバトルするようにしたい」

「プレイヤー1のカードをマウスでクリックして選択できるようにしたい」

「カードに画像を表示したい」

カード画像はStableDiffusionで生成しますが、サイズが小さいとコレジャナイ画像が生成されてしまうので、512 x 768で生成しました。

ゲームで表示するにはデカすぎるので縮小します。

画像変形ツールを使って縮小しておくのが常套手段ですが、

ここはあえてPythonでやります。

「カードの画像サイズを幅128、高さ192にしたい」

回答:

〜 Pillow(PIL)ライブラリを使用して画像をリサイズすることができます。

ここで、生成されたコードの動作には、追加のライブラリをインストールする作業が必要となりました。

Windowsならコマンドプロンプトから、Macならターミナルから、Pillowライブラリをインストールします。

pip install Pillow

うまくいかないときはググってみましょう。

まだまだchatGPT様のお世話になります。

canvasの背景色を深緑にしたい」

「カード画像5枚横に並べて表示し、クリックされたカード画像に外枠を表示、前にクリックし表示していた枠は消すようにしてください」

「それぞれのカード画像の下にランクを表示したい」

・・・

そんなこんなで出来上がった「ドラゴンカードゲーム」

ドラゴンカードゲーム
import tkinter as tk
from tkinter import messagebox
from tkinter import PhotoImage
from PIL import Image, ImageTk  # Pillowライブラリをインポート
import random

# カードデッキを作成
suits = ['ハート', 'ダイヤ', 'スペード', 'クラブ']
ranks = ['ドラコ', 'リザードマン', 'ドレイク', 'ヒドラ', 'サーペント', 'ワイバーン', 'アジュールドラゴン', 'クリムゾンドラゴン', '西洋ドラゴン', '東洋ドラゴン']
deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]
card_width = 128
card_height = 192
card_interval = 20
card_p1_y = 310
card_p2_y = 40

# プレイヤーを作成
player1 = {'名前': 'あなた', '手札': []}
player2 = {'名前': 'コンピュータ', '手札': []}

#プレイヤー1カード(表示用)
player1_cards = {}

# 選択されたカードを格納する変数
selected_card = None

# 外枠の色を指定
frame_color = "red"

# 選択されたカードのインデックスを保持する変数
selected_card_index = None

# ゲームの設定
rounds = 5  # ラウンド数を指定
round_num = 0
player1_score = 0
player2_score = 0

# 各ランクに対応する画像ファイルを辞書に格納
original_images = {}
card_images = {}
text_objects = []

# ゲームを初期化
def initialize_game():
    global round_num
    global player1_score
    global player2_score
    global player1_cards
    round_num = 0
    player1_score = 0
    player2_score = 0
    player1_cards = []
    player1['手札'] = []
    player2['手札'] = []
    deck = []
    deck = [{'スート': suit, 'ランク': rank} for suit in suits for rank in ranks]
    for _ in range(5):
        player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
        player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))

    # 選択されたカードをクリア
    global selected_card
    selected_card = None
    global selected_card_index
    canvas.delete(selected_card_index)
    selected_card_index = None
    global text_objects
    for text_object in text_objects:
        canvas.delete(text_object)
    text_objects = []

    #update_display()
    # プレイヤー1の手札を描画
    canvas.delete("player1_hand")
    x = card_interval
    for card in player1['手札']:
        draw_card(canvas, x, card_p1_y, card, 1)
        x += (card_width + card_interval)

    # プレイヤー2(コンピュータ)の手札を描画
    canvas.delete("player2_hand")
    x = card_interval
    for card in player2['手札']:
        draw_card(canvas, x, card_p2_y, card, 0)
        x += (card_width + card_interval)
    
    result_label1.config(text="")
    result_label2.config(text="")
    result_label3.config(text="")

# ゲームの進行
def play_round():
    global player1_score
    global player2_score
    global round_num

    if round_num > rounds:
        messagebox.showinfo("エラー", "「ゲームを始める」をクリックしてください")
        return

    # プレイヤー1(人間)がカードを選ぶ
    global selected_card  # グローバル変数として選択されたカードを保持
    if selected_card is None:
        messagebox.showinfo("エラー", "カードを選択してください。")
        return
    player1_card = selected_card

    round_num += 1

    # プレイヤー2(コンピュータ)が一番強いカードを選ぶ
    player2_card = select_strongest_card(player2['手札'])

    result_label1.config(text=f"{player2['名前']}のカード : {player2_card['ランク']} " + "★" * (ranks.index(player2_card['ランク']) + 1))
    result_label2.config(text=f"{player1['名前']}のカード : {player1_card['ランク']} " + "★" * (ranks.index(player1_card['ランク']) + 1))
    winner = battle(player1_card, player2_card)
    if winner == player1:
        player1_score += 1
        result_label3.config(text=f"{player1['名前']}の勝利!")
    elif winner == player2:
        player2_score += 1
        result_label3.config(text=f"{player2['名前']}の勝利!")
    else:
        result_label3.config(text="引き分け")

    if round_num >= rounds:
        round_num += 1
        # 最終結果を表示
        if player1_score > player2_score:
            messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n{player1['名前']}の勝利!")
        elif player1_score < player2_score:
            messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n{player2['名前']}の勝利!")
        else:
            messagebox.showinfo("ゲーム終了",f"結果発表\n{player1['名前']} : {player1_score}勝\n{player2['名前']} : {player2_score}勝\n引き分け!")
        return
    
    # プレイヤー1の手札から選択されたカードを削除
    player1['手札'].remove(selected_card)
    
    # プレイヤー2の手札から選択されたカードを削除
    player2['手札'].remove(player2_card)

    # カードをデッキからランダムに選び、手札に加える
    player1['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))
    player2['手札'].append(deck.pop(random.randint(0, len(deck) - 1)))

    # 選択されたカードをクリア
    selected_card = None
    global selected_card_index
    canvas.delete(selected_card_index)
    selected_card_index = None
    global text_objects
    for text_object in text_objects:
        canvas.delete(text_object)
    text_objects = []

    # プレイヤー1の手札を描画
    canvas.delete("player1_hand")
    x = card_interval
    for card in player1['手札']:
        draw_card(canvas, x, card_p1_y, card, 1)
        x += (card_width + card_interval)

    # プレイヤー2(コンピュータ)の手札を描画
    canvas.delete("player2_hand")
    x = card_interval
    for card in player2['手札']:
        draw_card(canvas, x, card_p2_y, card, 0)
        x += (card_width + card_interval)

def battle(player1_card, player2_card):
    global ranks
    if ranks.index(player1_card['ランク']) > ranks.index(player2_card['ランク']):
        return player1
    elif ranks.index(player1_card['ランク']) < ranks.index(player2_card['ランク']):
        return player2
    else:
        return None

def select_strongest_card(player_hand):
    global ranks
    strongest_card = None
    for card in player_hand:
        if not strongest_card or ranks.index(card['ランク']) > ranks.index(strongest_card['ランク']):
            strongest_card = card
    return strongest_card

def draw_card(canvas, x, y, card, sw):
    suit = card['スート']
    rank = card['ランク']
    rank_i = "★" * (ranks.index(rank) + 1)
    card_text = f"{rank}\n{rank_i}"

    # カード画像を配置
    card_object = canvas.create_image(x, y, anchor=tk.NW, image=card_images[rank])
    if sw > 0:
        canvas.tag_bind(card_object, "", select_card)

    # カードテキストを配置
    text_object = canvas.create_text(x, (y + card_height + 5), text=card_text, anchor="nw", fill="white", font=("Helvetica", 12))
    text_objects.append(text_object)

# クリックされたカードの処理
def select_card(event):
    global selected_card_index
    # 前に選択されたカードの外枠を削除
    if selected_card_index is not None:
        canvas.delete(selected_card_index)
    
    global selected_card
    x, y = event.x, event.y  # クリックされた位置のx座標とy座標を取得
    for card in player1['手札']:
        card_x, card_y = get_card_position(player1['手札'].index(card))
        if card_x <= x <= card_x + card_width and card_y <= y <= card_y + card_height:
            selected_card = card
            break

    # クリックされたカードの外枠を表示
    selected_card_index = canvas.create_rectangle((card_x - 2), (card_y - 2), (card_x + card_width + 2), (card_y + card_height + 2), outline=frame_color, width=2)

def get_card_position(index):
    x = card_interval + index * (card_width + card_interval)
    return x, card_p1_y

# Tkinterウィンドウを作成
root = tk.Tk()
root.title("カードバトルゲーム")

# Canvasを作成
canvas = tk.Canvas(root, width=800, height=550, bg="dark green")
canvas.pack()

# デフォルトカードに対応する画像ファイルを読み込む
default_card_image = Image.open('default.png')

# Stable Diffusion で生成した画像のサイズが大きいため縮小します
# 幅と高さ比2:3で512*768とかで生成(サイズ小さいとコレジャナイ画像になるため)
# 各ランクの画像ファイルを読み込み、Tkinter PhotoImageオブジェクトに変換して格納
for rank in ranks:
    image_file = f"{rank}.png"  # ランクごとの画像ファイル名を生成
    original_images[rank] = Image.open(image_file)

    # 対応するランクの画像を辞書から取得
    original_image = original_images.get(rank)
    
    if original_image:
        # 画像を指定のサイズにリサイズ
        resized_image = original_image.resize((card_width, card_height), Image.LANCZOS)

        # リサイズした画像をTkinter PhotoImageオブジェクトに変換
        card_images[rank] = ImageTk.PhotoImage(resized_image)
    else:
        # デフォルト画像を指定のサイズにリサイズ
        resized_image = default_card_image.resize((card_width, card_height), Image.LANCZOS)

        # リサイズした画像をTkinter PhotoImageオブジェクトに変換
        card_images[rank] = ImageTk.PhotoImage(resized_image)

#テキスト配置
canvas.create_text(card_interval, card_p1_y - 20, text=f"{player1['名前']}のカード", anchor="w", fill="white", font=("Helvetica", 14))
canvas.create_text(card_interval, card_p2_y - 20, text=f"{player2['名前']}のカード", anchor="w", fill="white", font=("Helvetica", 14))

# ボタンとラベルを配置
play_button = tk.Button(root, text="選んだカードで対決!", command=play_round)
play_button.pack()
start_button = tk.Button(root, text="ゲームを始める", command=initialize_game)
start_button.pack()
result_label1 = tk.Label(root, text="")
result_label1.pack()
result_label2 = tk.Label(root, text="")
result_label2.pack()
result_label3 = tk.Label(root, text="")
result_label3.pack()

# ゲームを初期化
initialize_game()

# Tkinterウィンドウを表示
root.mainloop()

生成されたコードを付け加えたり変更したりで大変だった感じはあるが

これを知識が無いところから作成することを考えたら

もう、素晴らしいね。

出来上がったコードは、まだ変なところとかあったり、

コンピュータのカードを隠せるようにするなど、改良の余地はあるけど、

なんか、標題の目的を達成できた感がある。

今回はドラゴンで男の子向けだが、女の子向けの画像も用意したいなと思っている。

オトナ向けは・・・やめておこう。