<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>NO MORE! 車輪の再発明</title><description>No description</description><link>https://mem-archive.com/</link><language>ja</language><item><title>OpenCode + Ollamaを使ってローカルLLMを使った開発環境を構築する</title><link>https://mem-archive.com/posts/2026/05/06/post-3403/</link><guid isPermaLink="true">https://mem-archive.com/posts/2026/05/06/post-3403/</guid><pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;そういえばコーディングエージェントでローカルLLMを使ったことないなと思ったので使ってみよう、と思ったのでそのための環境を構築してみます。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;p&gt;今回はMac上に環境を構築していきます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OS : macOS Sequoia&lt;/li&gt;
&lt;li&gt;CPU : Apple M4&lt;/li&gt;
&lt;li&gt;メモリ : 32GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;環境構築の手順&lt;/h2&gt;
&lt;h3&gt;Ollamaのインストール&lt;/h3&gt;
&lt;p&gt;まずはローカルLLMを動かすためにOllamaをインストールします。&lt;/p&gt;
&lt;p&gt;下記の公式ページを参照すると、&lt;code&gt;curl&lt;/code&gt;でインストールする方法とdmgファイルをダウンロードしてからインストールする方法の2つが記載されています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://ollama.com/download&quot;&gt;Download Ollama on macOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;今回はdmgファイルをダウンロードしてからインストールしました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ollama --version
ollama version is 0.23.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2026年5月5日現在だと、0.23.0がインストールされました。&lt;/p&gt;
&lt;p&gt;インストール後、Ollamaが正常に起動することを確認します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2026-05-05-18.44.03.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;ローカルLLMのモデルをダウンロード&lt;/h3&gt;
&lt;p&gt;利用するローカルLLMのモデルをダウンロードします。&lt;/p&gt;
&lt;p&gt;最近だとGemma 4の評判が良さそうなので、今回はこちらを利用してみます。&lt;/p&gt;
&lt;p&gt;Gemma 4にもいくつか種類があるみたいですがが、Macにはメモリを32GB積んでいるのでGemma 4 26Bが使い勝手が良さそうです。&lt;/p&gt;
&lt;p&gt;なのでこのモデルをダウンロードしてきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ollama pull gemma4:26b
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;OpenCodeのインストール&lt;/h3&gt;
&lt;p&gt;ローカルLLMに対応したコーディングエージェントをインストールします。&lt;/p&gt;
&lt;p&gt;今回は仕事でも使っていて個人的にお気に入りなOpenCodeをインストールします。&lt;/p&gt;
&lt;p&gt;homebrewを利用しているならコマンド一発でインストール可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install anomalyco/tap/opencode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事にインストールできていそうです。2026年5月5日現在では1.14.33がインストールされました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ opencode --version
1.14.33
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;OpenCodeでローカルLLMを利用するように設定する&lt;/h3&gt;
&lt;p&gt;必要なものはインストールできたので、OpenCodeでGemma 4を利用するための設定を作っていきます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.config/opencode/opencode.jsonc&lt;/code&gt;に設定を記述していきます。&lt;/p&gt;
&lt;p&gt;OpenCodeの公式にOllamaを利用する際の設定ファイルの例があるので、これをベースにモデルのみ変更します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://opencode.ai/docs/providers#ollama&quot;&gt;プロバイダー | OpenCode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;$schema&quot;: &quot;https://opencode.ai/config.json&quot;,
  &quot;provider&quot;: {
    &quot;ollama&quot;: {
      &quot;npm&quot;: &quot;@ai-sdk/openai-compatible&quot;,
      &quot;name&quot;: &quot;Ollama (local)&quot;,
      &quot;options&quot;: {
        &quot;baseURL&quot;: &quot;http://localhost:11434/v1&quot;
      },
      &quot;models&quot;: {
        &quot;gemma4:26b&quot;: { // ダウンロードしたモデルを指定
          &quot;name&quot;: &quot;gemma4:26b&quot;
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで準備は完了したので、次は実際にOpenCodeでGemma 4を動かしてみます。&lt;/p&gt;
&lt;h2&gt;動作確認&lt;/h2&gt;
&lt;h3&gt;OpenCodeの起動&lt;/h3&gt;
&lt;p&gt;AIにコーディングさせたいプロジェクトのディレクトリに移動して、OpenCodeを起動します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd &amp;lt;プロジェクトのディレクトリ&amp;gt;
$ opencode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると以下のようにCLI (TUI) 上でOpenCodeが起動します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2026-05-06-8.20.51.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/models&lt;/code&gt;と入力してEnterを押すとモデルを選択できるため、そこで&lt;code&gt;gemma4:26b&lt;/code&gt;を選択します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2026-05-06-8.23.43.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2026-05-06-8.23.53.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;これで、ローカルLLMのGemma 4を使ってOpenCodeを利用できるようになりました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2026-05-06-8.24.17.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.classmethod.jp/articles/ollama-opencode-setup&quot;&gt;OllamaとOpenCodeでローカルAIコーディング環境を構築してみた&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[Dart] mockitoを使ってユニットテストを実装する</title><link>https://mem-archive.com/posts/2025/03/08/post-3386/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/03/08/post-3386/</guid><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;flutterで開発中のアプリのユニットテストを書いていると、DBを利用しているようなクラスはモック化したくなりました。&lt;/p&gt;
&lt;p&gt;mockitoを使うと気軽にモックを作ることができるので、使い方のメモです。&lt;/p&gt;
&lt;h2&gt;mockitoの導入&lt;/h2&gt;
&lt;p&gt;まずは&lt;code&gt;pubspec.yml&lt;/code&gt;に必要なライブラリを追加していきます。&lt;/p&gt;
&lt;p&gt;モックを自動生成するには&lt;code&gt;mockito&lt;/code&gt;の他に&lt;code&gt;build_runner&lt;/code&gt;も必要になるので、この2つを追加します。&lt;/p&gt;
&lt;p&gt;下記のように&lt;code&gt;dev_dependencies&lt;/code&gt;に記述します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev_dependencies:
  flutter_test:
    sdk: flutter
  mockito: ^5.4.5 # 追加
  build_runner: ^2.4.14 # 追加
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;モックを作成する&lt;/h2&gt;
&lt;p&gt;今回は、&lt;code&gt;Hello!&lt;/code&gt;と返すだけの↓のシンプルなクラスのモックを作成してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Sample {
  String getMessage() {
    return &quot;Hello!&quot;;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;test/sample_test.dart&lt;/code&gt;というテストクラスを作成して、以下のように記述します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import &apos;package:flutter_sample/sample.dart&apos;;
import &apos;package:mockito/annotations.dart&apos;;

@GenerateNiceMocks([MockSpec&amp;lt;Sample&amp;gt;()])
void main() {
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ポイントは&lt;code&gt;@GenerateNiceMocks&lt;/code&gt;の行で、これを書くことでSampleクラスのモックを自動生成できるようになります。&lt;/p&gt;
&lt;p&gt;次に、下記のコマンドを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dart run build_runner build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると&lt;code&gt;test/sample_test.mocks.dart&lt;/code&gt;というファイルが生成されます。これがSampleクラスのモッククラスです。&lt;/p&gt;
&lt;p&gt;これでモッククラスを自動生成することができました。&lt;/p&gt;
&lt;h2&gt;モックを利用したユニットテストを作成する&lt;/h2&gt;
&lt;p&gt;モッククラスの生成ができたので、モックを使ったユニットテストを作成します。&lt;/p&gt;
&lt;p&gt;先ほど作成した&lt;code&gt;test/sample_test.dart&lt;/code&gt;にテストを書いていきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import &apos;package:flutter_sample/sample.dart&apos;;
import &apos;package:flutter_test/flutter_test.dart&apos;;
import &apos;package:mockito/annotations.dart&apos;;
import &apos;package:mockito/mockito.dart&apos;;

import &apos;sample_test.mocks.dart&apos;;

@GenerateNiceMocks([MockSpec&amp;lt;Sample&amp;gt;()])
void main() {
  test(&apos;test&apos;, () {
    Sample sample = MockSample(); // モックを作成
    when(sample.getMessage()).thenReturn(&apos;Hi!&apos;); // getMessage()を呼ぶと&quot;Hi!&quot;と返ってくるように設定

    expect(sample.getMessage(), &apos;Hi!&apos;);
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本来、&lt;code&gt;Sample&lt;/code&gt;クラスの&lt;code&gt;getMessage()&lt;/code&gt;を呼ぶと&quot;Hello!&quot;という文字列が返ってきますが、上記テストではモックを利用して&quot;Hi!&quot;という文字列が返るようにしています。&lt;/p&gt;
&lt;p&gt;このようにメソッドの返り値をモック化することで、DBやAPIなど利用しているメソッドの振る舞いを固定することができ、外部環境に依存しないユニットテストを作成することができます。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pub.dev/packages/mockito&quot;&gt;mockito | Dart package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/73089271/mockito-not-generating-mocks&quot;&gt;flutter - Mockito not generating mocks - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>mise(旧rtx)を利用して言語のバージョンを管理する</title><link>https://mem-archive.com/posts/2025/02/08/post-3375/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/02/08/post-3375/</guid><pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;もともとはjenvとかnodevとかを利用して、プログラミング言語のバージョンを切り替えていたのですが、PCを新調したのに合わせてmiseに一本化したので利用方法のメモです。&lt;/p&gt;
&lt;h2&gt;miseとは&lt;/h2&gt;
&lt;p&gt;jenvとかnodenvとかgoenvとか言語ごとにバージョン管理ツールが存在しますが、言語の数だけツールのインストール/設定が必要だったりで少々面倒でした。&lt;/p&gt;
&lt;p&gt;それを解決してくれるのがmiseです。miseは複数の言語のバージョンをまとめて管理してくれる素敵ツールです。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OS：macOS Sequoia&lt;/li&gt;
&lt;li&gt;Shell：zsh&lt;/li&gt;
&lt;li&gt;Homebrewのバージョン：4.4.20&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;インストール&lt;/h2&gt;
&lt;p&gt;今回はMacで環境構築しているので、homebrewで導入可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install mise
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.zshrc&lt;/code&gt;にアクティベート用の処理を書き込みます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo &apos;eval &quot;$(mise activate zsh)&quot;&apos; &amp;gt;&amp;gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで準備は完了です。&lt;/p&gt;
&lt;p&gt;ターミナルを再起動 or &lt;code&gt;$ source ~/.zshrc&lt;/code&gt;コマンドなので&lt;code&gt;.zshrc&lt;/code&gt;を再読み込みすれば反映されます。&lt;/p&gt;
&lt;h2&gt;使い方&lt;/h2&gt;
&lt;p&gt;例えばmiseでインストールできるJavaの一覧を取得したい場合は、下記コマンドで取得できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mise ls-remote java
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;カレントディレクトリでJava17を利用するには次のコマンドを実行します&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mise use java@17
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このとき、miseでJava17をインストールしていなかった場合はインストールも実行されます。&lt;/p&gt;
&lt;p&gt;グローバルでJava17を利用する場合には次のコマンドを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mise use --global java@17
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;他にも色々と便利なことができそうな雰囲気ですが、取り急ぎ以上を知っていれば各言語のバージョンをいい感じに管理できます。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mise.jdx.dev/getting-started.html&quot;&gt;Getting Started | mise-en-place&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/skyblue-jpn/items/58ad650f5a721f2ce890&quot;&gt;初心者の初心者による初心者のための開発環境セットアップツール mise 入門（updated from rtx） #Linux - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] テストカバレッジを確認する</title><link>https://mem-archive.com/posts/2025/02/01/post-3367/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/02/01/post-3367/</guid><pubDate>Sat, 01 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;flutterでユニットテストを書いていて、どのくらい網羅できたかの確認のためにテストカバレッジを確認したいことがあります。&lt;/p&gt;
&lt;p&gt;毎回どうやって確認するか忘れるので、確認方法のメモです。&lt;/p&gt;
&lt;h2&gt;事前準備&lt;/h2&gt;
&lt;p&gt;lcovをインストールします。環境はMacなのでhomebrewを使ってインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install lcov
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;テストカバレッジの確認方法&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--coverage&lt;/code&gt;オプションをつけてユニットテストを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ flutter test --coverage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;するとユニットテスト実行完了後、&lt;code&gt;coverage/lcov.info&lt;/code&gt;というファイルが作成されています。&lt;/p&gt;
&lt;p&gt;このファイルにテストカバレッジの情報が記述されていますが、このままだと見にくいので、次のコマンドでhtml形式に変換してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ genhtml coverage/lcov.info -o coverage/html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると&lt;code&gt;coverage/html/index.html&lt;/code&gt;というファイルが生成されるので、これをブラウザで開くとテストカバレッジを確認できます。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/Harx02663971/items/55e649adc0002c14912b&quot;&gt;【Flutter】テストカバレッジを効率よく出力する環境を整える&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>MacBook Air(Early 2015)にインストールしたLubuntu 24.04.1をネット接続する</title><link>https://mem-archive.com/posts/2025/01/11/post-3346/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/01/11/post-3346/</guid><pubDate>Sat, 11 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;h2&gt;目的&lt;/h2&gt;
&lt;p&gt;以前、古いMacBook AirにLubuntu 24.04.1をインストールしました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2024/12/19/post-3246/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;しかし、ただインストールしただけではWi-Fiに接続できずに流石にできることが限られてしまうので、今回はWi-Fiに接続してネット接続をするところまでをやってみます。&lt;/p&gt;
&lt;h2&gt;用意するもの&lt;/h2&gt;
&lt;p&gt;ネット接続に必要なドライバをインストールするためには、どうにかしてネットに接続しなければいけないというジレンマが発生します。&lt;/p&gt;
&lt;p&gt;そのため、USB→有線LANの変換アダプタを利用するか、USB接続の無線LANの子機を用意してネット接続する必要があります。&lt;/p&gt;
&lt;p&gt;いずれの場合でも、接続しただけでLubuntu上で正常に認識してくれるものを用意する必要があるので注意です。&lt;/p&gt;
&lt;p&gt;今回は、家に転がっていた無線LAN子機のBUFFALO WLI-UC-G301Nを利用します。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://kakaku.com/item/K0000044512&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;一時的に無線LAN子機を使ってWi-Fiに接続する&lt;/h3&gt;
&lt;p&gt;必要なドライバをインストールするため、用意した無線LANの子機をMacBook Airに接続します。&lt;/p&gt;
&lt;p&gt;今回用意した子機は接続するとLinuxでも自動で認識してくれるので、接続して画像のここの部分をクリックすると接続するWi-Fiを選択できるようになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;screen1_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;あとは接続したいWi-Fiを選択して、パスワードを入力すると無事Wi-Fiにつながります。&lt;/p&gt;
&lt;p&gt;接続が成功すると、↓画像のようなアイコンになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;screen2_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;MacBook内臓の無線LANモジュールでWi-Fiに接続する&lt;/h3&gt;
&lt;p&gt;さて、無事にネット接続はできましたが無線LAN子機を接続しっぱなしなのも使い勝手が悪いので、MacBook内臓の無線LANモジュールでWi-Fiに接続できるようにしていきます。&lt;/p&gt;
&lt;p&gt;まず、アプリケーションメニューから「システムツール → QTerminal」を選択して、ターミナルソフトを立ち上げます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;screen3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;そして、下記コマンドを実行してドライバをインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt update
$ sudo apt install bcmwl-kernel-source
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インストールが完了したら、無線LANの子機を抜いて一旦PCを再起動します。&lt;/p&gt;
&lt;p&gt;再起動後、無線LAN子機を外したのでWi-Fiの接続が切れていますが、ドライバをインストールしたため内臓の無線LANモジュールを利用してWi-Fiに接続できるようになっています。&lt;/p&gt;
&lt;p&gt;そのため、デスクトップ右下の画像部分をクリックすると接続するWi-Fiをできるようになっているので、接続したいWi-Fiを選択して、パスワードを入力すると無線LAN子機なしでも無事Wi-Fiに接続できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;screen1_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com/questions/1455500/macbook-air-2015-wifi-adapter-not-found-issue&quot;&gt;networking - Macbook Air 2015 wifi adapter not found issue - Ask Ubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[SpringBoot] ユニットテスト時にapplication.ymlの値を部分的に上書きする</title><link>https://mem-archive.com/posts/2025/01/07/post-3153/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/01/07/post-3153/</guid><pubDate>Mon, 06 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;仕事でSpringBootをよく使ってて、設定値をapplication.ymlから読み込んでいます。&lt;/p&gt;
&lt;p&gt;この設定値の一部のみをユニットテストのときのみ変えたいことがちょくちょくあるのですが、方法をいつも忘れるのでその方法のメモです。&lt;/p&gt;
&lt;p&gt;特定のユニットテストのみで設定値を変える方法と、ユニットテスト全体で設定値を変える方法の2つを紹介します。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;↓のような設定値を書いたapplication.ymlがあります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setting:
  value1: hoge # ← ユニットテストのときのみこの値を変更したい
  value2: fuga # ← この値は変更したくない
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これらの設定値を読み込んだ下記クラスもあります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SettingProperties {
    @Getter
    @Value(&quot;${setting.value1}&quot;)
    private String value1;

    @Getter
    @Value(&quot;${setting.value2}&quot;)
    private String value2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;↓のようなユニットテストを作成して、このユニットテスト内では&lt;code&gt;value1&lt;/code&gt;の値を変えたいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@SpringBootTest
class DemoApplicationTests {
	@Autowired
	SettingProperties properties;

	@Test
	void test() {
		Assertions.assertEquals(&quot;piyo&quot;, properties.getValue1()); // ← これが通るようにしたい
		Assertions.assertEquals(&quot;fuga&quot;, properties.getValue2());
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;特定のユニットテストのみで設定値を上書きする方法&lt;/h2&gt;
&lt;p&gt;特定のユニットテストのときの設定値の一部を上書きするには、&lt;code&gt;@ActiveProfiles&lt;/code&gt;というアノテーションを利用します。&lt;/p&gt;
&lt;p&gt;まず、&lt;code&gt;src/test/resources/application-test.yml&lt;/code&gt;というファイルを作成して、上書きしたい設定値の値のみを記載します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setting:
  value1: piyo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;次に、上書きした値を利用したいユニットテストで、&lt;code&gt;@ActiveProfiles&lt;/code&gt;アノテーションを追加します。アノテーションの引数には&lt;code&gt;application-&amp;lt;任意の値&amp;gt;.yml&lt;/code&gt;で指定した&lt;code&gt;&amp;lt;任意の値&amp;gt;&lt;/code&gt;部分の文字列を指定します。（今回の場合は&lt;code&gt;test&lt;/code&gt;）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@SpringBootTest
@ActiveProfiles(&quot;test&quot;) // ← この行を追加
class DemoApplicationTests {
	@Autowired
	SettingProperties properties;

	@Test
	void test() {
		Assertions.assertEquals(&quot;piyo&quot;, properties.getValue1());
		Assertions.assertEquals(&quot;fuga&quot;, properties.getValue2());
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると、このユニットテストは問題なく通るようになります。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://anveloper.com/2020/12/26/%E3%83%86%E3%82%B9%E3%83%88%E7%94%A8%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E5%88%86%E3%81%91%E3%82%8B-activeprofiles&quot;&gt;テスト用の設定を分ける – @ActiveProfiles – 諦めなければできる – Anveloper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ユニットテスト全体で設定値を上書きする方法&lt;/h2&gt;
&lt;p&gt;上述のように&lt;code&gt;@ActiveProfiles&lt;/code&gt;を利用すればユニットテストのときのみ特定の設定値を上書きすることができます。&lt;br /&gt;
しかし、ユニットテスト全体で特定の設定値を上書きした場合、ユニットテストの数が多いと全てに&lt;code&gt;@ActiveProfiles&lt;/code&gt;を記載するのは少々大変です。&lt;/p&gt;
&lt;p&gt;そこで、ユニットテスト全体で特定の設定値を上書きする方法も紹介します。&lt;/p&gt;
&lt;p&gt;まず、&lt;code&gt;src/test/resources/config/application.yml&lt;/code&gt;というファイルを作成して、上書きしたい設定値の値のみを記載します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setting:
  value1: piyo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;config&lt;/code&gt;という名前のディレクトリ直下に&lt;code&gt;application.yml&lt;/code&gt;を配置するのがポイントです。&lt;/p&gt;
&lt;p&gt;必要な修正はこれだけです。これだけで、ユニットテスト内では&lt;code&gt;SettingProperties::getValue1()&lt;/code&gt;を呼び出すと&lt;code&gt;piyo&lt;/code&gt;という文字列が返ってくるようになります。&lt;/p&gt;
&lt;p&gt;SpringBootではベースとなる&lt;code&gt;application.yml&lt;/code&gt;を読み込んだ後に、&lt;code&gt;config&lt;/code&gt;ディレクトリ内に&lt;code&gt;application.yml&lt;/code&gt;があればそれを追加で読み込むため、このような挙動になります。&lt;/p&gt;
&lt;p&gt;そのため、&lt;code&gt;src/test/resources/config/application.yml&lt;/code&gt;にユニットテスト用に上書きしたい設定を書けば、テスト全体でその値が利用されるようになります。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.files&quot;&gt;Externalized Configuration :: Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[Dart] 現在の年齢を計算する</title><link>https://mem-archive.com/posts/2025/01/03/post-3314/</link><guid isPermaLink="true">https://mem-archive.com/posts/2025/01/03/post-3314/</guid><pubDate>Fri, 03 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;現在開発中のflutterアプリで、生年月日から現在の年齢を計算する必要がありました。&lt;/p&gt;
&lt;p&gt;dartのDateTimeクラスにはdifferenceメソッドがありますが、これだと差分の日数は取得できますが、年数は取得できません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.flutter.dev/flutter/dart-core/DateTime/difference.html&quot;&gt;difference method - DateTime class - dart:core library - Dart API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;差分日数を365で割れば年齢計算できるかなと思ったものの、閏年を考えるとうまくいかないパターンもあるので、どうにかして閏年を踏まえてシンプルに年齢を計算したいです。&lt;/p&gt;
&lt;h2&gt;実装&lt;/h2&gt;
&lt;p&gt;現在の日時から年齢を計算するメソッドを作成しました。これで閏年の場合も年齢を計算できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int getNowAge(DateTime birthday) {
  DateTime now = DateTime.now();

  int age = now.year - birthday.year; // 1
  if (now.month * 100 + now.day &amp;lt; birthday.month * 100 + birthday.day) { // 2
    age--;
  }
  return age;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解説&lt;/h2&gt;
&lt;p&gt;まず1では単純に現在の西暦から生年月日の西暦を引いています。&lt;/p&gt;
&lt;p&gt;この場合、今年の誕生日をまだ迎えていない場合は1歳余分に足された値になっているため、今年の誕生日を迎えたかどうかを2のif文で行なって、迎えていない場合は年齢を1引いています。&lt;/p&gt;
&lt;p&gt;今年の誕生日を迎えたかどうか、月を比較してから日を比較して・・・みたいなことをするのは面倒なので、MMDDの整数値の形にして比較することでロジックをシンプルにしています。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/aqril_1132/items/46e824541e657190ef3e&quot;&gt;生年月日から年齢を簡易計算する数式 #Python - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>古いMacBook AirにLubuntuをインストールする</title><link>https://mem-archive.com/posts/2024/12/19/post-3246/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/12/19/post-3246/</guid><pubDate>Thu, 19 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;h2&gt;目的&lt;/h2&gt;
&lt;p&gt;こちらで書いたように、現在サポートの切れたMacBook Airを所持しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2024/12/08/post-3155/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;使えないMacBookを持っていても仕方ないので、Ubuntuの派生で軽量なディストリビューションであるLubuntuをこのMacBook Airに入れてみようと思います。&lt;/p&gt;
&lt;p&gt;完全にmacOSを消すわけではなくて、macOSとLubuntuのデュアルブートを目指します。&lt;/p&gt;
&lt;h2&gt;対象のMacBook Air&lt;/h2&gt;
&lt;p&gt;持っているのはMacBook Air Early 2015で、次のようなスペックになっています。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;スペック名&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;スペック&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;macOS Monterey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Intel Core i5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;メモリ&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ストレージ&lt;/td&gt;
&lt;td&gt;256GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;ISOイメージを書き込んだUSBメモリの作成&lt;/h2&gt;
&lt;h3&gt;ISOファイルのダウンロード&lt;/h3&gt;
&lt;p&gt;以下リンクから、LubuntuのISOイメージをダウンロードします。&lt;br /&gt;
2024年12月現在だと、24.04.1が最新のLTS版なので、これをダウンロードします。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://lubuntu.me/downloads&quot;&gt;Downloads – Lubuntu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;念のため、ダウンロードしたISOファイルのハッシュ値が&lt;a href=&quot;https://cdimage.ubuntu.com/lubuntu/releases/24.04.1/release/SHA256SUMS&quot;&gt;公式のもの&lt;/a&gt;と一致するか確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ shasum -a 256 ~/Downloads/lubuntu-24.04.1-desktop-amd64.iso
cba3737f2ec5e1432e86d0f95fb09e1e4febeca97cc7d2d2c56c788a6903b345  /Users/user/Downloads/lubuntu-24.04.1-desktop-amd64.iso
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ISOファイルを書き込んだUSBメモリを作成&lt;/h3&gt;
&lt;p&gt;マウントしてるディスクの一覧を取得して、USBメモリを特定します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *251.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                 Apple_APFS Container disk1         250.8 GB   disk0s2

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme - +250.8 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume System - Data           130.7 GB   disk1s1
   2:                APFS Volume Preboot                 814.8 MB   disk1s2
   3:                APFS Volume Recovery                1.1 GB     disk1s3
   4:                APFS Volume VM                      2.1 GB     disk1s4
   5:                APFS Volume System                  15.4 GB    disk1s5
   6:              APFS Snapshot com.apple.os.update-... 15.4 GB    disk1s5s1

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                            USB4G                  *4.0 GB     disk2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;USBメモリは/dev/disk2ですね。&lt;/p&gt;
&lt;p&gt;次にUSBメモリを初期化します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ diskutil eraseDisk MS-DOS USB4GB /dev/disk2
Started erase on disk2 (USB4G)
Unmounting disk
Creating the partition map
Waiting for partitions to activate
Formatting disk2s2 as MS-DOS (FAT) with name USB4GB
512 bytes per physical sector
/dev/rdisk2s2: 7403360 sectors in 925420 FAT32 clusters (4096 bytes/cluster)
bps=512 spc=8 res=32 nft=2 mid=0xf8 spt=32 hds=255 hid=411648 drv=0x80 bsec=7417856 bspf=7230 rdcl=2 infs=1 bkbs=6
Mounting disk
Finished erase on disk2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初期化完了後にアンマウントします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ diskutil unmountDisk /dev/disk2
Unmount of all volumes on disk2 was successful
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ISOファイルをUSBメモリに書き込みます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ISOファイルのパスは相対パスだとNo such file or directoryになったのでフルパスで指定
$ sudo dd if=/Users/user/Downloads/lubuntu-24.04.1-desktop-amd64.iso of=/dev/disk2 bs=16m status=progress
  3305111552 bytes (3305 MB, 3152 MiB) transferred 7118.291s, 464 kB/s
197+1 records in
197+1 records out
3309283328 bytes transferred in 7124.972452 secs (464463 bytes/sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ブロックサイズの設定が悪かったのか、低速のUSBメモリを使ったからなのか、思いのほか時間がかかった・・・&lt;/p&gt;
&lt;p&gt;完了したらUSBを取り外します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ diskutil eject /dev/disk2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでLubuntuブート用のUSBメモリの完成です。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.mothule.com/linux/linux-write-iso-to-usb-flash-drive&quot;&gt;MacでLinuxのISOファイルをUSBフラッシュメモリに書き込む - もちゅろぐ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Lubuntu用のボリュームの作成&lt;/h2&gt;
&lt;p&gt;MacBookのローカルディスクのパーティションを分割して、Lubuntuインストール用のパーティションを作成します。&lt;/p&gt;
&lt;p&gt;Macにプリインストールされているディスクユーティリティを開いて、Partitionを選択します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-18-at-23.56.54.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;「+」ボタンを押します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-18-at-23.59.53.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Add Partitionを選択します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-19-at-0.37.24.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;新規パーティションの情報を入力します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-19-at-0.41.39.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;各項目はそれぞれ次のように入力します。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;項目&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;入力する値&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;適当なパーティション名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format&lt;/td&gt;
&lt;td&gt;MS-DOS (FAT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;パーティションのサイズ   円グラフの分割の線をドラッグすることで変更する&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Applyボタンを押すと確認モーダルが表示されるので、内容が問題なければPartitionを押します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-19-at-0.47.19.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ボリュームのリサイズ中は電源落とすなよ的なモーダルが出るので、Continueを押すとパーティションの作成が始まります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-19-at-0.49.11.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;しばらく待つとパーティションの作成が完了しました。&lt;br /&gt;
（最後にアンマウントが失敗した的なエラーが出ましたが、気にせず進めます。）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2024-12-19-at-1.44.55.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://yna87.hatenablog.com/entry/2020/02/17/094912&quot;&gt;macOS CatalinaとUbuntuのデュアルブートにした話&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Lubuntuのインストール&lt;/h2&gt;
&lt;p&gt;(ここからはスクショ取れてないので画像なしです・・・)&lt;/p&gt;
&lt;p&gt;MacBookを一度シャットダウンして、ISOイメージを書き込んだUSBメモリを挿します。&lt;/p&gt;
&lt;p&gt;optionキーを押しながらMacBookを再度起動するといつもと違う画面が表示されるので、「EFI Boot」を選択します。&lt;/p&gt;
&lt;p&gt;すると真っ黒い画面が表示されるので、矢印キーで「Try or Install Lubuntu」を選択してEnterを押します。&lt;/p&gt;
&lt;p&gt;Lubuntuの起動画面が表示されるので、Select Your Languageで日本語を選択して、「Install Lubuntu」をクリックします。&lt;/p&gt;
&lt;p&gt;Lubuntuのインストーラが起動するので、あとは流れに沿ってインストールするだけです。&lt;/p&gt;
&lt;p&gt;注意するべき点として、流れに沿って進めていくとインストールするパーティションを選択する画面が出てきます。&lt;br /&gt;
ここでは「パーティションの置換」を選択して、先ほど作成したパーティションを選択します。&lt;br /&gt;
ここの選択をミスるとmacOSが消えたりします。&lt;/p&gt;
&lt;p&gt;インストールが完了すると再起動を促されます。&lt;/p&gt;
&lt;p&gt;今すぐ再起動を選択すると「Please remove the installation medium, then press ENTER:」と表示された画面になるので、USBメモリを取り除いてEnterを押します。&lt;/p&gt;
&lt;p&gt;するとLubuntuが起動するようになります。&lt;/p&gt;
&lt;p&gt;Lubuntuインストール完了後はデフォルトでLubuntuが起動するようになります。macOSを起動したい場合は、optionを押しながら起動して、「System」を選択するとmacOSが起動します。&lt;/p&gt;
&lt;p&gt;とりあえずLubuntuのインストールまでは完了しました。ネットにはまだ繋がってなかったり、実用のためにはまだ諸々設定が必要ですが、その辺りは次回やっていきます。&lt;/p&gt;
&lt;p&gt;ネットに接続したときの記事はこちら：&lt;br /&gt;
&amp;lt;LinkCard url=&quot;/2025/01/11/post-3346/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/Yasu31/items/b24efc9986899f5aabbf&quot;&gt;macOS MojaveとUbuntu 18.04をデュアルブート(新しいMac等は非対応)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>MacBookのCTO(カスタマイズ)モデルを安く買う方法調査メモ</title><link>https://mem-archive.com/posts/2024/12/16/post-3185/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/12/16/post-3185/</guid><pubDate>Mon, 16 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;h2&gt;目的&lt;/h2&gt;
&lt;p&gt;この間、いろいろと限界がきている開発環境をMacBookに一本化することを決めました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2024/12/08/post-3155/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;さて、そう決めたのはいいもののMacBookはお値段がお高い。&lt;/p&gt;
&lt;p&gt;カスタマイズしない吊るしのモデルであれば売っているお店も多く、たまにセールで安くなってたりしそうですが、カスタマイズモデルとなるとそもそも購入できるところが限られており、安く買う難易度は高そうです。&lt;/p&gt;
&lt;p&gt;そして用途的にはカスタマイズしたMacBookが欲しいので、どうやったら安く買えるかを調査しました。&lt;/p&gt;
&lt;h2&gt;購入したいMacbook&lt;/h2&gt;
&lt;p&gt;自分の用途を考えて、次のスペックのMacbook Proを狙うことにしました。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;スペック名&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;スペック&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;機種&lt;/td&gt;
&lt;td&gt;MacBook Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;M4 10コアCPU 10コアGPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;メモリ&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ストレージ&lt;/td&gt;
&lt;td&gt;1TB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;吊るしのMacBook Proのメモリだけ32GBに増やしたモデルです。&lt;/p&gt;
&lt;p&gt;これをApple公式で買うと338,800円するので、これをベースにどこまでお得にできるか調べます。&lt;/p&gt;
&lt;h2&gt;安く買う方法の調査&lt;/h2&gt;
&lt;p&gt;Apple公式とAmazon、主要な家電量販店の通販サイトだとどの程度ポイント/割引があるかを調べました。&lt;/p&gt;
&lt;p&gt;Yahoo!ショッピングや楽天はCTOモデルの扱いがそもそもなさそうだったので除外しています。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;購入場所&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;価格&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;ポイントや割引&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apple公式&lt;/td&gt;
&lt;td&gt;338,800円&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;Amazonポイント：1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ビックカメラ.com&lt;/td&gt;
&lt;td&gt;338,800円&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：3%   PayPayクーポン：2000円分&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ソフマップ&lt;/td&gt;
&lt;td&gt;338,800円&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   ハピタス経由で購入：1.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ヤマダウェブコム&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：1.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;エディオン&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Joshin web&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：1.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ノジマオンライン&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：1.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;コジマネット&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;家電量販店のポイント：5%   モッピー経由で購入：2.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ケーズデンキオンラインショップ&lt;/td&gt;
&lt;td&gt;該当モデルなし&lt;/td&gt;
&lt;td&gt;Rakuten Rebates経由：1.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;こうしてまとめてみると、家電量販店の通販サイトで購入すると、5%はポイントバックされそうです。&lt;/p&gt;
&lt;p&gt;さらに、ポイントサイトを経由して購入すれば、ポイントサイトのポイントが数%分ゲットできそうです。&lt;/p&gt;
&lt;p&gt;今回調査した限りでは、ビックカメラの通販サイトで購入するのが最も還元率が高そうです。&lt;br /&gt;
ちょうど狙ってたモデルをビックカメラであれば販売していたので、ちょうどよいです。&lt;br /&gt;
ついでに期間限定のPayPayクーポン2000円分も出ていて、さらにお得に買えそうです。&lt;/p&gt;
&lt;p&gt;※ ポイントサイト経由の還元率は割と頻繁に変動していて、どのポイントサイト経由が一番お得かは日々変動しているので、購入前に確認した方が良いです。&lt;/p&gt;
&lt;h2&gt;実際に買ってみた&lt;/h2&gt;
&lt;p&gt;お高い買い物でしたが、ビックカメラ.comで狙ったスペックのMacBook Proを購入しました。&lt;/p&gt;
&lt;p&gt;お値段としては338,800円で、以下のポイントを獲得しました。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;ポイント&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;獲得ポイント数&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PayPayクーポン利用分&lt;/td&gt;
&lt;td&gt;2,000ポイント&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ビックカメラ&lt;/td&gt;
&lt;td&gt;16,940ポイント&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;モッピー   ※ 実際に買おうとしたら還元率が3.5%にアップしてました&lt;/td&gt;
&lt;td&gt;10,780ポイント&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;PayPayクレジットカードで支払ったので、実際はクレカポイント1%分がここに上乗せされるのですが、このポイントはApple公式で購入しても付与されるポイントなので省略しています。&lt;/p&gt;
&lt;p&gt;ということで、29,720円相当のポイント分、約8.8%ほどお得に購入することができました。&lt;/p&gt;
</content:encoded></item><item><title>歴代MacBook Pro/AirのOSのサポート期間の調査</title><link>https://mem-archive.com/posts/2024/12/14/post-3208/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/12/14/post-3208/</guid><pubDate>Sat, 14 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2026/5/24追記&lt;br /&gt;
思ったよりもこの記事が読まれているみたいなので、2026年5月24日時点の最新情報に更新しました&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;前回、いろいろと限界がきている開発環境をMacBookに一本化することを決めました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2024/12/08/post-3155/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;次に考えるべきは、MacBook ProとMacBook Airのどちらを購入するかです。&lt;/p&gt;
&lt;p&gt;購入したらなるべく長く使いたいと考えているのですが、ふとProとAirだとどっちの方がOSのサポート期間が長いのか気になったので調べてみました。&lt;/p&gt;
&lt;p&gt;なるべくApple公式情報を参照していますが、サイレントリリースされたモデルなどもあり、一部それ以外の情報源が混じっています。&lt;/p&gt;
&lt;h2&gt;最新OS提供期間まとめ&lt;/h2&gt;
&lt;h3&gt;macOSのリリース日&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;バージョン&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;リリース日&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS 26 (Tahoe)&lt;/td&gt;
&lt;td&gt;2025/9/15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 15 (Sequoia)&lt;/td&gt;
&lt;td&gt;2024/9/16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 14 (Sonoma)&lt;/td&gt;
&lt;td&gt;2023/9/26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 13 (Ventura)&lt;/td&gt;
&lt;td&gt;2022/10/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;2021/10/25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 11 (Big Sur)&lt;/td&gt;
&lt;td&gt;2020/11/12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 10.15 (Catalina)&lt;/td&gt;
&lt;td&gt;2019/10/7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 10.14 (Mojave)&lt;/td&gt;
&lt;td&gt;2018/9/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 10.13 (High Sierra)&lt;/td&gt;
&lt;td&gt;2017/9/25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS 10.12 (Sierra)&lt;/td&gt;
&lt;td&gt;2016/9/20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;※ Apple公式の情報をなるべく参照するようにしているので、時差の関係で日本だとリリース日が1日ずれてる可能性があります。&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;参考&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apple.com/jp/newsroom/2025/09/new-versions-of-apples-software-platforms-are-now-available/&quot;&gt;https://www.apple.com/jp/newsroom/2025/09/new-versions-of-apples-software-platforms-are-now-available/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2024/09/macos-sequoia-is-available-today&quot;&gt;https://www.apple.com/newsroom/2024/09/macos-sequoia-is-available-today&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2023/09/macos-sonoma-is-available-today&quot;&gt;https://www.apple.com/newsroom/2023/09/macos-sonoma-is-available-today&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2022/10/macos-ventura-is-now-available&quot;&gt;https://www.apple.com/newsroom/2022/10/macos-ventura-is-now-available&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available&quot;&gt;https://www.apple.com/newsroom/2021/10/macos-monterey-is-now-available&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2020/11/macos-big-sur-is-here&quot;&gt;https://www.apple.com/newsroom/2020/11/macos-big-sur-is-here&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2019/10/macos-catalina-is-available-today&quot;&gt;https://www.apple.com/newsroom/2019/10/macos-catalina-is-available-today&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/newsroom/2018/09/macos-mojave-is-available-today&quot;&gt;https://www.apple.com/newsroom/2018/09/macos-mojave-is-available-today&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/103806&quot;&gt;https://support.apple.com/ja-jp/103806&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/103424&quot;&gt;https://support.apple.com/ja-jp/103424&lt;/a&gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h3&gt;MacBook Pro&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;世代&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;発売日&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;最終OS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;最新OS提供期間&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2025, M5&lt;/td&gt;
&lt;td&gt;2025/10/22&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024, M4&lt;/td&gt;
&lt;td&gt;2024/11/8&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023, M3&lt;/td&gt;
&lt;td&gt;2023/11/7&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022, M2&lt;/td&gt;
&lt;td&gt;2023/2/3&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021, M1&lt;/td&gt;
&lt;td&gt;2021/10/26&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020, M1&lt;/td&gt;
&lt;td&gt;2020/11/17&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;2020/5/4&lt;/td&gt;
&lt;td&gt;macOS 15 (Sequoia) /macOS 26 (Tahoe)&lt;/td&gt;
&lt;td&gt;1959日   ※ macOS 15 (Sequoia)が最終OSとして計算&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2019/11/14&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2019/7/9&lt;/td&gt;
&lt;td&gt;macOS 15 (Sequoia)&lt;/td&gt;
&lt;td&gt;2259日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2019/5/21&lt;/td&gt;
&lt;td&gt;macOS 15 (Sequoia)&lt;/td&gt;
&lt;td&gt;2308日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;2018/7/12&lt;/td&gt;
&lt;td&gt;macOS 15 (Sequoia)&lt;/td&gt;
&lt;td&gt;2621日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;2017/6/7&lt;/td&gt;
&lt;td&gt;macOS 13 (Ventura)&lt;/td&gt;
&lt;td&gt;2301日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;2016/10/28&lt;/td&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;2186日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2015&lt;/td&gt;
&lt;td&gt;2015/5/9&lt;/td&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;2724日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early 2015&lt;/td&gt;
&lt;td&gt;2015/3/10&lt;/td&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;2784日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2014&lt;/td&gt;
&lt;td&gt;2014/7/29&lt;/td&gt;
&lt;td&gt;macOS 11 (Big Sur)&lt;/td&gt;
&lt;td&gt;2644日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Late 2013&lt;/td&gt;
&lt;td&gt;2013/10/23&lt;/td&gt;
&lt;td&gt;macOS 11 (Big Sur)&lt;/td&gt;
&lt;td&gt;2923日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early 2013&lt;/td&gt;
&lt;td&gt;2013/2/13&lt;/td&gt;
&lt;td&gt;macOS 10.15 (Catalina)&lt;/td&gt;
&lt;td&gt;2828日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Late 2012&lt;/td&gt;
&lt;td&gt;2012/10/24&lt;/td&gt;
&lt;td&gt;macOS 10.15 (Catalina)&lt;/td&gt;
&lt;td&gt;2940日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2012&lt;/td&gt;
&lt;td&gt;2012/6/12&lt;/td&gt;
&lt;td&gt;macOS 10.15 (Catalina)&lt;/td&gt;
&lt;td&gt;3074日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Late 2011&lt;/td&gt;
&lt;td&gt;2011/10/24&lt;/td&gt;
&lt;td&gt;macOS 10.13 (High Sierra)&lt;/td&gt;
&lt;td&gt;2526日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early 2011&lt;/td&gt;
&lt;td&gt;2011/2/24&lt;/td&gt;
&lt;td&gt;macOS 10.13 (High Sierra)&lt;/td&gt;
&lt;td&gt;2768日&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;※ Apple公式の情報をなるべく参照するようにしているので、時差の関係で日本だとリリース日が1日ずれてる可能性があります。&lt;/p&gt;
&lt;p&gt;※ Intel Mac 2020にはmacOS 15 (Sequoia)が最終OSのモデルとmacOS 26 (Tahoe)が最終OSのモデルがあります。&lt;/p&gt;
&lt;p&gt;最新OS提供期間は、対象のMacBookを最新のOSの状態にできた日数です。&lt;br /&gt;
例えば、Mid 2015のMacBook Proであれば、macOS 12が最終OSなので、macOS 13がリリースされる前日までは最新状態にできたと判断して、Mid 2015の発売日からmacOS 13リリースの前日までの日数で計算しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;参考：MacBook Proリリース日&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apple.com/jp/newsroom/2025/10/apple-unveils-new-14-inch-macbook-pro-powered-by-the-m5-chip/&quot;&gt;https://www.apple.com/jp/newsroom/2025/10/apple-unveils-new-14-inch-macbook-pro-powered-by-the-m5-chip/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2024/10/new-macbook-pro-features-m4-family-of-chips-and-apple-intelligence&quot;&gt;https://www.apple.com/jp/newsroom/2024/10/new-macbook-pro-features-m4-family-of-chips-and-apple-intelligence&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2023/10/apple-unveils-new-macbook-pro-featuring-m3-chips&quot;&gt;https://www.apple.com/jp/newsroom/2023/10/apple-unveils-new-macbook-pro-featuring-m3-chips&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2023/01/apple-unveils-macbook-pro-featuring-m2-pro-and-m2-max&quot;&gt;https://www.apple.com/jp/newsroom/2023/01/apple-unveils-macbook-pro-featuring-m2-pro-and-m2-max&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2021/10/apple-unveils-game-changing-macbook-pro&quot;&gt;https://www.apple.com/jp/newsroom/2021/10/apple-unveils-game-changing-macbook-pro&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2020/11/introducing-the-next-generation-of-mac&quot;&gt;https://www.apple.com/jp/newsroom/2020/11/introducing-the-next-generation-of-mac&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2020/05/apple-updates-13-inch-macbook-pro-with-magic-keyboard-double-the-storage-and-faster-performance&quot;&gt;https://www.apple.com/jp/newsroom/2020/05/apple-updates-13-inch-macbook-pro-with-magic-keyboard-double-the-storage-and-faster-performance&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2019/11/apple-introduces-16-inch-macbook-pro-the-worlds-best-pro-notebook&quot;&gt;https://www.apple.com/jp/newsroom/2019/11/apple-introduces-16-inch-macbook-pro-the-worlds-best-pro-notebook&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2019/07/macbook-air-and-macbook-pro-updated-for-back-to-school-season&quot;&gt;https://www.apple.com/jp/newsroom/2019/07/macbook-air-and-macbook-pro-updated-for-back-to-school-season&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2019/05/apple-introduces-first-8-core-macbook-pro-the-fastest-mac-notebook-ever&quot;&gt;https://www.apple.com/jp/newsroom/2019/05/apple-introduces-first-8-core-macbook-pro-the-fastest-mac-notebook-ever&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2018/07/apple-updates-macbook-pro-with-faster-performance-and-new-features-for-pros&quot;&gt;https://www.apple.com/jp/newsroom/2018/07/apple-updates-macbook-pro-with-faster-performance-and-new-features-for-pros&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2017/06/imac-receives-major-update-featuring-more-powerful-graphics-faster-processors-thunderbolt-3-brighter-displays&quot;&gt;https://www.apple.com/jp/newsroom/2017/06/imac-receives-major-update-featuring-more-powerful-graphics-faster-processors-thunderbolt-3-brighter-displays&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2016/10/apple-unveils-groundbreaking-new-macbook-pro&quot;&gt;https://www.apple.com/jp/newsroom/2016/10/apple-unveils-groundbreaking-new-macbook-pro&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2015/03/09Apple-Updates-13-inch-MacBook-Pro-with-Retina-Display-MacBook-Air&quot;&gt;https://www.apple.com/jp/newsroom/2015/03/09Apple-Updates-13-inch-MacBook-Pro-with-Retina-Display-MacBook-Air&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2015/05/19Apple-Introduces-15-inch-MacBook-Pro-with-Force-Touch-Trackpad-New-1-999-iMac-with-Retina-5K-Display&quot;&gt;https://www.apple.com/jp/newsroom/2015/05/19Apple-Introduces-15-inch-MacBook-Pro-with-Force-Touch-Trackpad-New-1-999-iMac-with-Retina-5K-Display&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2014/07/29Apple-Updates-MacBook-Pro-with-Retina-Display&quot;&gt;https://www.apple.com/jp/newsroom/2014/07/29Apple-Updates-MacBook-Pro-with-Retina-Display&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2013/10/23MacBook-Pro-with-Retina-Display-Updated-with-Latest-Processors-Faster-Graphics-Longer-Battery-Life&quot;&gt;https://www.apple.com/jp/newsroom/2013/10/23MacBook-Pro-with-Retina-Display-Updated-with-Latest-Processors-Faster-Graphics-Longer-Battery-Life&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2013/02/13Apple-Updates-Processors-Prices-of-MacBook-Pro-with-Retina-Display&quot;&gt;https://www.apple.com/jp/newsroom/2013/02/13Apple-Updates-Processors-Prices-of-MacBook-Pro-with-Retina-Display&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2012/06/11Apple-Introduces-All-New-MacBook-Pro-with-Retina-Display&quot;&gt;https://www.apple.com/jp/newsroom/2012/06/11Apple-Introduces-All-New-MacBook-Pro-with-Retina-Display&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2012/10/23Apple-Introduces-13-inch-MacBook-Pro-with-Retina-Display&quot;&gt;https://www.apple.com/jp/newsroom/2012/10/23Apple-Introduces-13-inch-MacBook-Pro-with-Retina-Display&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://everymac.com/systems/apple/macbook_pro/specs/macbook-pro-core-i5-2.4-13-late-2011-unibody-thunderbolt-specs.html#:~:text=Click%20on%20a%20category%20for,All%20Macs%20introduced%20in%202011.&quot;&gt;https://everymac.com/systems/apple/macbook_pro/specs/macbook-pro-core-i5-2.4-13-late-2011-unibody-thunderbolt-specs.html#:~:text=Click%20on%20a%20category%20for,All%20Macs%20introduced%20in%202011.&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2011/02/24Apple-Updates-MacBook-Pro-with-Next-Generation-Processors-Graphics-Thunderbolt-I-O-Technology&quot;&gt;https://www.apple.com/jp/newsroom/2011/02/24Apple-Updates-MacBook-Pro-with-Next-Generation-Processors-Graphics-Thunderbolt-I-O-Technology&lt;/a&gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;参考：最終OS&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/122867&quot;&gt;macOS Tahoe 26に対応しているコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/120282&quot;&gt;macOS Sequoia に対応しているコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/105113&quot;&gt;macOS Sonoma に対応しているコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/102861&quot;&gt;macOS Ventura に対応しているコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/103260&quot;&gt;macOS Monterey と互換性のあるコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/103111&quot;&gt;macOS Big Sur と互換性のあるコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/111930&quot;&gt;macOS Mojave - 技術仕様 - Apple サポート (日本)&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://support.apple.com/ja-jp/111934&quot;&gt;macOS High Sierra - 技術仕様 - Apple サポート (日本)&lt;/a&gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h3&gt;MacBook Air&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;世代&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;発売日&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;最終OS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;最新OS提供期間&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026, M5&lt;/td&gt;
&lt;td&gt;2026/3/11&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025, M4&lt;/td&gt;
&lt;td&gt;2025/3/12&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024, M3&lt;/td&gt;
&lt;td&gt;2024/3/4&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023, M2&lt;/td&gt;
&lt;td&gt;2023/6/13&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2022, M2&lt;/td&gt;
&lt;td&gt;2022/7/15&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020, M1&lt;/td&gt;
&lt;td&gt;2020/11/10&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;最新OS提供中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2020&lt;/td&gt;
&lt;td&gt;2020/3/18&lt;/td&gt;
&lt;td&gt;macOS 15 (Sequoia)&lt;/td&gt;
&lt;td&gt;2006日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;2019/7/9&lt;/td&gt;
&lt;td&gt;macOS 14 (Sonoma)&lt;/td&gt;
&lt;td&gt;1895日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;2018/10/30&lt;/td&gt;
&lt;td&gt;macOS 14 (Sonoma)&lt;/td&gt;
&lt;td&gt;2147日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;2017/6/5&lt;/td&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;1966日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early 2015&lt;/td&gt;
&lt;td&gt;2015/3/10&lt;/td&gt;
&lt;td&gt;macOS 12 (Monterey)&lt;/td&gt;
&lt;td&gt;2784日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early 2014&lt;/td&gt;
&lt;td&gt;2014/4/29&lt;/td&gt;
&lt;td&gt;macOS 11 (Big Sur)&lt;/td&gt;
&lt;td&gt;2735日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2013&lt;/td&gt;
&lt;td&gt;2013/6/11&lt;/td&gt;
&lt;td&gt;macOS 11 (Big Sur)&lt;/td&gt;
&lt;td&gt;3057日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2012&lt;/td&gt;
&lt;td&gt;2012/6/12&lt;/td&gt;
&lt;td&gt;macOS 10.15 (Catalina)&lt;/td&gt;
&lt;td&gt;3074日&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mid 2011&lt;/td&gt;
&lt;td&gt;2011/7/20&lt;/td&gt;
&lt;td&gt;macOS 10.13 (High Sierra)&lt;/td&gt;
&lt;td&gt;2622日&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;※ Apple公式の情報をなるべく参照するようにしているので、時差の関係で日本だとリリース日が1日ずれてる可能性があります。&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;参考：MacBook Airリリース日&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.apple.com/jp/newsroom/2026/03/apple-introduces-the-new-macbook-air-with-m5/&quot;&gt;https://www.apple.com/jp/newsroom/2026/03/apple-introduces-the-new-macbook-air-with-m5/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2025/03/apple-introduces-the-new-macbook-air-with-the-m4-chip-and-a-sky-blue-color/&quot;&gt;https://www.apple.com/jp/newsroom/2025/03/apple-introduces-the-new-macbook-air-with-the-m4-chip-and-a-sky-blue-color/&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2024/03/apple-unveils-the-new-13-and-15-inch-macbook-air-with-the-powerful-m3-chip&quot;&gt;https://www.apple.com/jp/newsroom/2024/03/apple-unveils-the-new-13-and-15-inch-macbook-air-with-the-powerful-m3-chip&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2023/06/apple-introduces-the-15-inch-macbook-air&quot;&gt;https://www.apple.com/jp/newsroom/2023/06/apple-introduces-the-15-inch-macbook-air&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2022/07/all-new-macbook-air-with-m2-available-to-order-starting-friday-july-8&quot;&gt;https://www.apple.com/jp/newsroom/2022/07/all-new-macbook-air-with-m2-available-to-order-starting-friday-july-8&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2020/11/introducing-the-next-generation-of-mac&quot;&gt;https://www.apple.com/jp/newsroom/2020/11/introducing-the-next-generation-of-mac&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2020/03/new-macbook-air-has-more-to-love-and-is-now-just-999&quot;&gt;https://www.apple.com/jp/newsroom/2020/03/new-macbook-air-has-more-to-love-and-is-now-just-999&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2019/07/macbook-air-and-macbook-pro-updated-for-back-to-school-season&quot;&gt;https://www.apple.com/jp/newsroom/2019/07/macbook-air-and-macbook-pro-updated-for-back-to-school-season&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2018/10/all-new-macbook-air-takes-flight&quot;&gt;https://www.apple.com/jp/newsroom/2018/10/all-new-macbook-air-takes-flight&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.gizmodo.jp/2017/06/hardware-announced-at-wwdc-2017.html&quot;&gt;https://www.gizmodo.jp/2017/06/hardware-announced-at-wwdc-2017.html&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2015/03/09Apple-Updates-13-inch-MacBook-Pro-with-Retina-Display-MacBook-Air&quot;&gt;https://www.apple.com/jp/newsroom/2015/03/09Apple-Updates-13-inch-MacBook-Pro-with-Retina-Display-MacBook-Air&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2014/04/29Apple-Updates-MacBook-Air&quot;&gt;https://www.apple.com/jp/newsroom/2014/04/29Apple-Updates-MacBook-Air&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2013/06/10Apple-Brings-All-Day-Battery-Life-to-MacBook-Air&quot;&gt;https://www.apple.com/jp/newsroom/2013/06/10Apple-Brings-All-Day-Battery-Life-to-MacBook-Air&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2012/06/11Apple-Updates-MacBook-Air-and-Current-Generation-MacBook-Pro-with-Latest-Processors-and-New-Graphics&quot;&gt;https://www.apple.com/jp/newsroom/2012/06/11Apple-Updates-MacBook-Air-and-Current-Generation-MacBook-Pro-with-Latest-Processors-and-New-Graphics&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.apple.com/jp/newsroom/2011/07/20Apple-Updates-MacBook-Air-With-Next-Generation-Processors-Thunderbolt-I-O-Backlit-Keyboard&quot;&gt;https://www.apple.com/jp/newsroom/2011/07/20Apple-Updates-MacBook-Air-With-Next-Generation-Processors-Thunderbolt-I-O-Backlit-Keyboard&lt;/a&gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h2&gt;最新OS提供期間の比較&lt;/h2&gt;
&lt;p&gt;表のままだと分かりづらいので、MacBook ProとAirの最新OS提供期間を年単位に直してグラフにプロットしてみました。&lt;/p&gt;
&lt;p&gt;Appleでは最新のOS + 2世代前のOSまではセキュリティアップデートを提供してくてるので、実際に↓のグラフ+2年ほどが問題なくMacBookを使える期間になります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;こうしてみると、最近は以前よりもMacBookのOSのサポート期間は短めの傾向となっていそうです。&lt;/p&gt;
&lt;p&gt;これはApple Siliconへの移行過渡期でIntel版MacBookのサポートが早めに切られているからなのか、それとももう以前より長い期間サポートしてくれなくなったのか。&lt;br /&gt;
前者であることを祈るばかり・・・&lt;/p&gt;
&lt;p&gt;そしてグラフを見る限りだと、ProとAirで最新OS提供期間に大きな差はなさそうに見えます。&lt;br /&gt;
ただし、2019/2020年のIntel MacBook Proは一部macOS 26 (Tahoe)まで対応していますが、Intel MacBook AirはmacOS 15 (Sequoia)で打ち切られていることを踏まえるとIntel MacについてはMacBook Proの方がサポート期間は手厚そうです。&lt;/p&gt;
&lt;p&gt;Apple Silicon搭載モデルについては、まだサポートが切られたものがないため、どの程度サポートしてくれるかは未知数ですね。&lt;/p&gt;
</content:encoded></item><item><title>【2024年】開発環境の刷新を考える</title><link>https://mem-archive.com/posts/2024/12/08/post-3155/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/12/08/post-3155/</guid><pubDate>Sun, 08 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;目的&lt;/h2&gt;
&lt;p&gt;現在開発に利用しているPCが結構古く、いろいろと限界が見えているので、PCの買い替えを検討しています。&lt;/p&gt;
&lt;p&gt;いくつかパターンが考えられるので、各パターンを整理してどのパターンを採用するのか決めたいです。&lt;/p&gt;
&lt;h2&gt;現在の開発環境&lt;/h2&gt;
&lt;p&gt;現在はメインとサブのPCの2台があるのですが、どちらも結構古めの構成です。&lt;/p&gt;
&lt;h3&gt;メインPC&lt;/h3&gt;
&lt;p&gt;メインの開発ではWindowsのデスクトップPCを利用しています。&lt;/p&gt;
&lt;p&gt;2017年頃に購入したBTOパソコンで、スペックは次の通りです。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;スペック名&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;スペック&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Windows 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Ryzen 7 1700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;メモリ&lt;/td&gt;
&lt;td&gt;32GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ストレージ&lt;/td&gt;
&lt;td&gt;Cドライブ：SSD 1TB   その以外：SSD 合計436GB、HDD 2TB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;サブPC&lt;/h3&gt;
&lt;p&gt;サブのPCとして、MacBook Airを利用しています。&lt;/p&gt;
&lt;p&gt;世代的にはEarly 2015なので、2015年頃のモデルです。&lt;/p&gt;
&lt;p&gt;開発したiOSアプリの動作確認や、実家に帰省するときの作業用PCとして利用しています。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;スペック名&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;スペック&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;macOS Monterey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;Intel Core i5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;メモリ&lt;/td&gt;
&lt;td&gt;8GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ストレージ&lt;/td&gt;
&lt;td&gt;256GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;現在の開発環境の問題点&lt;/h2&gt;
&lt;h3&gt;OSのサポートがそろそろやばい&lt;/h3&gt;
&lt;h4&gt;Windows&lt;/h4&gt;
&lt;p&gt;Windows 10のセキュリティサポートは2025年10月に終了するので、サポート終了まであと1年を切っています。&lt;/p&gt;
&lt;p&gt;なのでWindows 11にアップデートしたいのですが、残念ながらRyzen 7 1700はWindows 11に対応しておらず、正攻法ではアップデートできません。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/ja-jp/windows-hardware/design/minimum/supported/windows-11-22h2-supported-amd-processors&quot;&gt;Windows プロセッサ要件 - Windows 11 バージョン 22H2 および 23H2 でサポートされている AMD プロセッサ | Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Mac&lt;/h4&gt;
&lt;p&gt;現在の最新OSはmacOS Sequoiaですが、Early 2015世代のMacBook AirではSequoiaどころかMontereyまでしかアップデートできません。&lt;/p&gt;
&lt;p&gt;バージョニングでいうと、SequoiaがmacOS 15でMontereyがmacOS 12なので、3世代前ですね。&lt;/p&gt;
&lt;p&gt;macOSは最新世代+2世代前までのOSはセキュリティアップデートをしてくれているので、アウトですね。&lt;/p&gt;
&lt;p&gt;実際Montereyは2024年7月が最後のセキュリティアップデートとなっています。&lt;/p&gt;
&lt;p&gt;なので最近はもうMacBook Airは利用していません。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/103260&quot;&gt;macOS Monterey と互換性のあるコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/102861&quot;&gt;macOS Ventura に対応しているコンピュータ - Apple サポート (日本)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.apple.com/ja-jp/100100&quot;&gt;Appleのセキュリティリリース - Apple サポート (日本)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Apple Storeで公開しているアプリを更新できない&lt;/h3&gt;
&lt;p&gt;macOS Montereyでは最新のXcodeを入れることができません。&lt;/p&gt;
&lt;p&gt;その影響で公開しているアプリの更新しようとしても、Xcodeのバージョンが古いと怒られて更新できず、Apple Storeで公開しているアプリの更新が実質止まってしまっています。&lt;/p&gt;
&lt;h3&gt;iOSの動作確認が重すぎる&lt;/h3&gt;
&lt;p&gt;MacBook Air最大の役割が、iOSアプリをビルドして動作確認することなのですが、これが重すぎてぶっちゃけもう使えないレベルです。&lt;/p&gt;
&lt;h2&gt;刷新パターン&lt;/h2&gt;
&lt;h3&gt;WindowsデスクトップとMacbookの両方を買い替える&lt;/h3&gt;
&lt;p&gt;持ってる2台のPCを両方買い替えて、Windowsデスクトップ + MacBook環境の新環境を作る案です。&lt;/p&gt;
&lt;h4&gt;メリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;WindowsとMacの両方の環境を維持できる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;デメリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;お金がいっぱいかかる&lt;/li&gt;
&lt;li&gt;サブPCの稼働率は低くなるのでコスパ的には微妙？&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;WindowsノートとMac miniに買い替える&lt;/h3&gt;
&lt;p&gt;現在はデスクトップはWindows、ノートはMacという構成ですが、逆にデスクトップはMac、ノートはWindowsの環境に刷新する案です。&lt;/p&gt;
&lt;h4&gt;メリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;WindowsとMacの両方の環境を維持できる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;デメリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;お金がいっぱいかかる&lt;/li&gt;
&lt;li&gt;サブPCの稼働率は低くなるのでコスパ的には微妙？ただWindowノートはお手頃なのも多いのでMacBookを遊ばせておくよりはコスパよさそう&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Macのみ買い替えてMac1台に集約する&lt;/h3&gt;
&lt;p&gt;MacBookを購入してWindowsは引退させて、すべての作業をMacに集約する案です。&lt;/p&gt;
&lt;h4&gt;メリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;全作業を1台に集約できて効率的&lt;/li&gt;
&lt;li&gt;デスクトップを引退させられるので省スペースになる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;デメリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;1台に集約するとなるとそれなりのスペックが必要になるので、お金もそれなりにかかる&lt;/li&gt;
&lt;li&gt;Windowsを気軽に触れなくなる（Parallel Desktopとかを利用すれば仮想環境上にWindowsを維持することはできそう）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;メインPCの延命 + AWSのMacインスタンスの利用&lt;/h3&gt;
&lt;p&gt;メインPCでWindows 10を脱却して寿命を延ばして、Macを利用したいときはAWSのMacインスタンスを利用する案です。&lt;/p&gt;
&lt;p&gt;メインPCの延命策としては以下が考えられそうです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux化する&lt;/li&gt;
&lt;li&gt;非正攻法でWindows 11にアップデートする&lt;/li&gt;
&lt;li&gt;CPUだけWindows 11対応のものに交換して、正攻法でWindows 11にアップデートする&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;メリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;初期コスト的な面だと金はあまりかからない&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;デメリット&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Mac環境が必要になるたびに課金が必要&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mac環境を使い終わったときにちゃんと手順踏まないとうっかり高額請求されそう&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MacBookは引退させるので持ち運びできるPCがなくなる（Linux化してモバイルノートとして使うのはありかも）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;結論&lt;/h2&gt;
&lt;p&gt;「MacBookに一本化」案を採用することにしました。&lt;/p&gt;
&lt;p&gt;なので今使ってるのWindowsデスクトップとMacBook Airを引退させて、新しくMacBookに一本化していくことにします。&lt;/p&gt;
&lt;p&gt;WindowsとMacの両方使える自分でいたかったので「Windowsノート + Mac mini」案も悩んだんですが、どんなときでもMacBook1台あれば完結する魅力が上回りました。&lt;/p&gt;
&lt;p&gt;Windows環境はMacBook上の仮想環境を作ろうと思います。&lt;/p&gt;
</content:encoded></item><item><title>[Ghostscript] PDFを圧縮する</title><link>https://mem-archive.com/posts/2024/09/24/post-3138/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/09/24/post-3138/</guid><pubDate>Tue, 24 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;昨今、PDFのファイルサイズを小さくする様々なオンラインサービスがあります。&lt;/p&gt;
&lt;p&gt;しかし、個人情報の含まれるPDFだったりを小さくしたい場合、オンライン上にファイルをアップロードするのには抵抗があります。&lt;/p&gt;
&lt;p&gt;なので、オフラインでPDFファイルサイズを縮小できる方法を探すと、Ghostscriptを使うとできそうなことが分かったので試してみました。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;p&gt;以下の環境で実施しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;WSL2 Ubuntu-20.04&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;手順&lt;/h2&gt;
&lt;h3&gt;Ghostscriptをインストール&lt;/h3&gt;
&lt;p&gt;WSL2上のUbuntuにGhostscriptをインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt install ghostscript
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ghostscript 9.50がインストールされました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gs --version
9.50
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;PDFのファイルサイズを小さくする&lt;/h3&gt;
&lt;p&gt;いよいよファイルサイズを縮小します。以下のコマンドでファイルサイズを小さくできます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=&amp;lt;出力先のファイルパス&amp;gt; &amp;lt;小さくしたいPDFのファイルパス&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;試しにこちらの3.9Mあるinput.pdfというPDFのファイルサイズの縮小を試みてみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh input.pdf 
-rwxr-xr-x 1 user user 3.9M Sep 17 02:15 input.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;縮小実行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dQUIET -dBATCH -sOutputFile=output.pdf input.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.5kまでファイルサイズを圧縮できました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh output.pdf 
-rw-r--r-- 1 user user 2.5K Sep 17 02:20 output.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;コマンドの解説&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;オプション&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;説明&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-sDEVICE=pdfwrite&lt;/td&gt;
&lt;td&gt;PDF形式で出力します。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-dCompatibilityLevel=1.4&lt;/td&gt;
&lt;td&gt;PDFのバージョンを指定します。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-dPDFSETTINGS=/ebook&lt;/td&gt;
&lt;td&gt;PDFの解像度を指定します。   以下4つの値を指定できて、下に行くほど品質が高くなります。   ・/screen   ・/ebook   ・/printer   ・/prepress      指定しない場合は次の値を指定したときと同じ挙動になります。   ・/default      なお、入力したPDFから品質をなるべく落としたくない場合は、/prepressではなく/defaultを使用するべきらしいです。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-dNOPAUSE&lt;/td&gt;
&lt;td&gt;各ファイルを処理するたびに一時停止しないようになります。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-dQUIET&lt;/td&gt;
&lt;td&gt;標準出力への出力を抑制します。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-dBATCH&lt;/td&gt;
&lt;td&gt;対話形式に入るのではなく、コマンド実行完了後に処理を終了します。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-sOutputFile=&amp;lt;出力先のファイルパス&amp;gt;&lt;/td&gt;
&lt;td&gt;縮小後のPDFの出力先のファイルパスを指定します。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/JeJeNeNo/items/0f757f2261820167159d&quot;&gt;PDFのファイルサイズをGhostscriptのコマンドで小さくする #Linux - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ghostscript.com/docs/9.54.0/VectorDevices.htm&quot;&gt;High Level Output Devices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ghostscript.com/docs/9.54.0/Devices.htm&quot;&gt;Details of Ghostscript Output Devices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ghostscript.com/docs/9.54.0/Use.htm&quot;&gt;How to Use Ghostscript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] TextFormFieldを利用して「 &apos;initialValue == null || controller == null&apos;: is not true.」というエラーになった時の対応</title><link>https://mem-archive.com/posts/2024/07/20/post-3134/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/07/20/post-3134/</guid><pubDate>Sat, 20 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;flutterでアプリ開発してたらエラーではまったのでその対応メモです。&lt;br /&gt;
なお開発環境は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;flutter 3.22.2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;事象&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;TextFormField&lt;/code&gt;を利用すると、次のようなエラーが表示されました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;package:flutter/src/material/text_form_field.dart&apos;: Failed assertion: line 177 pos 15: &apos;initialValue == null || controller == null&apos;: is not true.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このエラーが発生する最小のコードは次の通りです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TextFormField(
  controller: TextEditingController(),
  initialValue: &apos;init&apos;,
),
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;TextFormField&lt;/code&gt;では&lt;code&gt;controller&lt;/code&gt;と&lt;code&gt;initialValue&lt;/code&gt;を同時に利用することはできません。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;initialValue&lt;/code&gt;に値を設定すると内部的にcontrollerを生成するため、同時に利用できないものと思われます。&lt;/p&gt;
&lt;h2&gt;対応方法&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;controller&lt;/code&gt;と&lt;code&gt;initialValue&lt;/code&gt;のどちらかのみを利用することでエラーを回避できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TextEditingController&lt;/code&gt;には初期値を設定することができるため、基本的には&lt;code&gt;controller&lt;/code&gt;を利用するで問題ないと思います。&lt;/p&gt;
&lt;h3&gt;修正前&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextFormField(
  controller: TextEditingController(),
  initialValue: &apos;init&apos;,
),
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;修正後&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextFormField(
  controller: TextEditingController(text: &apos;init&apos;),
),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.flutter.dev/flutter/material/TextFormField-class.html&quot;&gt;TextFormField class - material library - Dart API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/52691887/flutter-initialvalue-null-controller-null-is-not-true-error&quot;&gt;dart - Flutter - &apos;initialValue == null || controller == null&apos;: is not true. error - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] TextFieldのsuffixを常時表示させる</title><link>https://mem-archive.com/posts/2024/07/14/post-3125/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/07/14/post-3125/</guid><pubDate>Sun, 14 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;TextField&lt;/code&gt;の&lt;code&gt;suffix&lt;/code&gt;や&lt;code&gt;suffixText&lt;/code&gt;を使ってsuffixを設定すると、&lt;code&gt;TextField&lt;/code&gt;がフォーカスされたときにしか設定したsuffixが表示されません。&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./textfield_suffix1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figcaption&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TextField&lt;/code&gt;がfocusされた状態&lt;/p&gt;
&lt;p&gt;&amp;lt;/figcaption&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./textfield_suffix2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figcaption&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TextField&lt;/code&gt;がunfocusされた状態&lt;/p&gt;
&lt;p&gt;&amp;lt;/figcaption&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;これを、フォーカスされているかどうかに関わらず、常にsuffixが表示された状態にしたいです。&lt;/p&gt;
&lt;h2&gt;実装&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;suffixIcon&lt;/code&gt;は常時表示されるWidgetなので、&lt;code&gt;suffix&lt;/code&gt;や&lt;code&gt;suffixText&lt;/code&gt;の代わりにこれを使います。なお、環境は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dart 3.4.0&lt;/li&gt;
&lt;li&gt;Flutter 3.22.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;before：フォーカスを外すとsuffixが消える状態&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  decoration: InputDecoration(
    suffix: const Text(&apos;円&apos;),
  ),
),
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;after：フォーカスを外してもsuffixが消えない&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  decoration: InputDecoration(
    suffixIcon: const Text(&apos;円&apos;),
    suffixIconConstraints: BoxConstraints(minHeight: 0, minWidth: 0),
  ),
),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/58819979/is-there-a-way-to-create-a-text-field-with-a-prefix-that-is-always-visible-in-fl&quot;&gt;Is there a way to create a text field with a prefix that is always visible in flutter - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] BottomNavigationBarのlabelを非表示にする</title><link>https://mem-archive.com/posts/2024/06/03/post-3108/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/06/03/post-3108/</guid><pubDate>Sun, 02 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;BottomNavigationBar&lt;/code&gt;を利用して画面下にタブを実装したいです。タブにはアイコンのみ表示したいです。&lt;/p&gt;
&lt;p&gt;つまりこれのラベルを消して&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./bottom_navigation_bar.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;こうしたいです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./bottom_navigation_bar2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;実装&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Scaffold&lt;/code&gt;の&lt;code&gt;bottomNavigationBar&lt;/code&gt;を使ってラベルなしの下タブを実装します。なお、環境は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dart 3.4.0&lt;/li&gt;
&lt;li&gt;Flutter 3.22.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;before : ラベル表示&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: &apos;ほーむ&apos;),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: &apos;せってぃんぐ&apos;),
        ],
      ),
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;after : ラベル非表示&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: &apos;ほーむ&apos;),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: &apos;せってぃんぐ&apos;),
        ],
        showSelectedLabels: false,   // &amp;lt;- 追加
        showUnselectedLabels: false, // &amp;lt;- 追加
      ),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2行追加しただけです。具体的に何をやっているかは次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;showSelectedLabels: false&lt;/code&gt;：選択されているタブのラベルを非表示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;showUnselectedLabels: false&lt;/code&gt;：選択されていないタブのラベルを非表示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これですべてのタブのラベルが非表示になるため、アイコンのみ表示することができます。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/53941837/bottomnavigationbar-without-text&quot;&gt;flutter - BottomNavigationBar without Text - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] Error: The class &apos;FileSystemEvent&apos; can&apos;t be extended, implemented, or mixed in outside of its library because it&apos;s a sealed class. の対処メモ</title><link>https://mem-archive.com/posts/2024/05/19/post-3089/</link><guid isPermaLink="true">https://mem-archive.com/posts/2024/05/19/post-3089/</guid><pubDate>Sun, 19 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;flutterでアプリ開発してたらエラーではまったのでその対応メモです。&lt;/p&gt;
&lt;p&gt;なお開発環境は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;flutter 3.22.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;事象&lt;/h2&gt;
&lt;p&gt;mockを更新しようと次のコマンドを実行すると&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dart run build_runner build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以下のようなエラーが出て失敗しました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Resolving dependencies in `E:\sample_project`...
Downloading packages...
Got dependencies in `E:\sample_project`.
Building package executable... (1.0s)
Failed to build build_runner:build_runner:
/C:/Users/User/AppData/Local/Pub/Cache/hosted/pub.dev/watcher-1.0.2/lib/src/constructable_file_system_event.dart:7:57: Error: The class &apos;FileSystemEvent&apos; can&apos;t be extended, implemented, or mixed in outside of its library because it&apos;s a sealed class.
abstract class _ConstructableFileSystemEvent implements FileSystemEvent {
                                                        ^
Failed to build build_runner:build_runner:
/C:/Users/User/AppData/Local/Pub/Cache/hosted/pub.dev/watcher-1.0.2/lib/src/constructable_file_system_event.dart:7:57: Error: The class &apos;FileSystemEvent&apos; can&apos;t be extended, implemented, or mixed in outside of its library because it&apos;s a sealed class.
abstract class _ConstructableFileSystemEvent implements FileSystemEvent {
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;watcher 1.0.2を利用しているのが原因です。現時点の最新バージョンであるwatcher 1.1.0にバージョンを上げると解決しました。&lt;/p&gt;
&lt;h2&gt;対応方法&lt;/h2&gt;
&lt;p&gt;pubspec.lockで指定されているwatcherのバージョンを1.1.0に上げられればいいわけです。&lt;/p&gt;
&lt;p&gt;pubspec.ymlでバージョンを固定していないなら、次のコマンドで手っ取り早くwatcherのバージョンだけ更新できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ flutter pub upgrade watcher
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更新後のpubspec.lockを確認すると、watcher 1.1.0に上がって問題のエラーが出なくなっていると思います。&lt;/p&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dart-lang/sdk/issues/52570&quot;&gt;Watcher fails with &lt;code&gt;class &apos;FileSystemEvent&apos; can&apos;t be extended&lt;/code&gt;, for dart, not for flutter. · Issue #52570 · dart-lang/sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>[flutter] TextFieldでよく使う機能/デザインメモ</title><link>https://mem-archive.com/posts/2023/12/12/post-3067/</link><guid isPermaLink="true">https://mem-archive.com/posts/2023/12/12/post-3067/</guid><pubDate>Tue, 12 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;よく使うのに毎回忘れるのでメモ&lt;/p&gt;
&lt;h2&gt;複数行入力する&lt;/h2&gt;
&lt;h3&gt;3行まで表示&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  keyboardType: TextInputType.multiline,
  maxLines: 3,

)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./textfield_maxline3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;行数制限なしで表示&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  keyboardType: TextInputType.multiline,
  maxLines: null,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./textfield_maxline_null.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;下線を表示しない&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  decoration: InputDecoration(
     border: InputBorder.none,
  ),
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./no_line-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;プレースホルダーを表示する&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;TextField(
  decoration: InputDecoration(
    hintText: &apos;プレースホルダー&apos;, // プレースホルダーに表示した文字列
    hintStyle: TextStyle(
      color: Colors.grey, // プレースホルダーの文字列の色
    )
  ),
),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./textfield_placeholder.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://417.run/pg/flutter-dart/flutter-textfield-multiline&quot;&gt;【Flutter】TextFieldで改行する方法 | 417.Run()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/OzWay_Jin/items/60c90ff297aec4ac743c&quot;&gt;Flutter TextField　デザイン　サンプル集 #UI - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>MacにVespaをインストールして動作確認するまで</title><link>https://mem-archive.com/posts/2023/01/05/post-3008/</link><guid isPermaLink="true">https://mem-archive.com/posts/2023/01/05/post-3008/</guid><pubDate>Thu, 05 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;最近検索周りの勉強を初めました。今回はOSSの検索エンジンであるVespaの環境をMac上に構築して動かしてみたいと思います。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://github.com/vespa-engine/vespa&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Vespaのインストール&lt;/h2&gt;
&lt;p&gt;以下のQuick Startに従ってインストールを進めます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vespa.ai/en/vespa-quick-start.html&quot;&gt;Deploy an application locally | Vespa. Big data. Real time. Open source.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;必要なスペックを満たしているか確認&lt;/h3&gt;
&lt;p&gt;Dockerに割り当てられているメモリを確認します。Vespaを動かすには最低4GBのメモリが必要らしいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker info | grep &quot;Total Memory&quot;
 Total Memory: 3.842GiB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3.842GiB...。GB単位に直すとギリギリ4GB超えてるので大丈夫なはず。&lt;/p&gt;
&lt;h3&gt;vespa-cliのインストール&lt;/h3&gt;
&lt;p&gt;Vespaのコマンドラインツールをインストールします。Macではhomebrewで簡単にインストールできます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install vespa-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Vespaの初期設定&lt;/h3&gt;
&lt;p&gt;今回はローカル上に環境を構築するためtargetをローカルに設定します。ヘルプを見た感じデフォルトで&lt;code&gt;local&lt;/code&gt;に設定されてそうでしたが、公式のQuick Startに書いてあったので一応実行しておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa config set target local
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;VespaのDockerコンテナを起動&lt;/h3&gt;
&lt;p&gt;VespaのDockerコンテナを起動させます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker run --detach --name vespa --hostname vespa-container \
  --publish 8080:8080 --publish 19071:19071 \
  vespaengine/vespa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでVespaを使う下準備ができました。Vespaが正常に起動できているか確認します。以下のように表示されたらOKです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa status deploy --wait 300
Waiting up to 5m0s for deploy service to become available ...
Deploy API at http://127.0.0.1:19071 is ready
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;サンプルアプリケーションを動かしてみる&lt;/h2&gt;
&lt;p&gt;Vespaのサンプルアプリケーションを動かしてみます。&lt;/p&gt;
&lt;h3&gt;サンプルアプリのクローン&lt;/h3&gt;
&lt;p&gt;サンプルアプリケーションはGitHubの以下のリポジトリで管理されています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://github.com/vespa-engine/sample-apps&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vespa clone&lt;/code&gt;コマンドを使えば上記リポジトリの特定のサンプルアプリをローカルにクローンすることができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa clone &amp;lt;サンプルアプリのパス&amp;gt; &amp;lt;ローカルのクローン先のパス&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回はQuick Startに従って&lt;code&gt;album-recommendation&lt;/code&gt;というサンプルアプリをクローンします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa clone album-recommendation myapp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;myapp&lt;/code&gt;というディレクトリに無事クローンされました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls myapp/
README.md	ext		schemas		services.xml	tests
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;サンプリアプリのデプロイ&lt;/h3&gt;
&lt;p&gt;クローンしてきた&lt;code&gt;album-recommendation&lt;/code&gt;をVespaにデプロイします。コマンドを実行して以下のような表示になれば成功です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd myapp
$ vespa deploy --wait 300
Uploading application package ... done

Success: Deployed .

Waiting up to 5m0s for query service to become available ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;デプロイが成功すると、Vespaに以下のスキーマが反映されます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vespa-engine/sample-apps/blob/master/album-recommendation/app/schemas/music.sd&quot;&gt;sample-apps/album-recommendation/app/schemas/music.sd at master · vespa-engine/sample-apps · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;サンプルデータのフィード&lt;/h3&gt;
&lt;p&gt;サンプルデータをフィードします。成功すると以下のようにSuccessと表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa document ext/A-Head-Full-of-Dreams.json &amp;amp;&amp;amp; \
  vespa document ext/Love-Is-Here-To-Stay.json &amp;amp;&amp;amp; \
  vespa document ext/Hardwired...To-Self-Destruct.json
Success: put id:mynamespace:music::a-head-full-of-dreams
Success: put id:mynamespace:music::love-id-here-to-stay
Success: put id:mynamespace:music::hardwired-to-self-destruct
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでVespaに実際にデータを入れるところまで完了しました。&lt;/p&gt;
&lt;h3&gt;検索してみる&lt;/h3&gt;
&lt;p&gt;いよいよ検索してみます。&lt;/p&gt;
&lt;p&gt;今回は、&lt;code&gt;music&lt;/code&gt;というスキーマの&lt;code&gt;album&lt;/code&gt;というフィールドに、&lt;code&gt;head&lt;/code&gt;という文言が含まれているデータを検索してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vespa query &quot;select * from music where album contains &apos;head&apos;&quot; language=en-US
{
    &quot;root&quot;: {
        &quot;id&quot;: &quot;toplevel&quot;,
        &quot;relevance&quot;: 1.0,
        &quot;fields&quot;: {
            &quot;totalCount&quot;: 1
        },
        &quot;coverage&quot;: {
            &quot;coverage&quot;: 100,
            &quot;documents&quot;: 3,
            &quot;full&quot;: true,
            &quot;nodes&quot;: 1,
            &quot;results&quot;: 1,
            &quot;resultsFull&quot;: 1
        },
        &quot;children&quot;: [
            {
                &quot;id&quot;: &quot;id:mynamespace:music::a-head-full-of-dreams&quot;,
                &quot;relevance&quot;: 0.16343879032006284,
                &quot;source&quot;: &quot;music&quot;,
                &quot;fields&quot;: {
                    &quot;sddocname&quot;: &quot;music&quot;,
                    &quot;documentid&quot;: &quot;id:mynamespace:music::a-head-full-of-dreams&quot;,
                    &quot;artist&quot;: &quot;Coldplay&quot;,
                    &quot;album&quot;: &quot;A Head Full of Dreams&quot;,
                    &quot;year&quot;: 2015,
                    &quot;category_scores&quot;: {
                        &quot;type&quot;: &quot;tensor&amp;lt;float&amp;gt;(cat{})&quot;,
                        &quot;cells&quot;: {
                            &quot;pop&quot;: 1.0,
                            &quot;rock&quot;: 0.20000000298023224,
                            &quot;jazz&quot;: 0.0
                        }
                    }
                }
            }
        ]
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;はい、無事検索結果が取得できました。&lt;br /&gt;
（&lt;code&gt;album&lt;/code&gt;は正規化されているため、大文字/小文字を区別せず検索されます。）&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vespa.ai/en/query-language.html&quot;&gt;Vespa Query Language - YQL | Vespa. Big data. Real time. Open source.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.vespa.ai/en/linguistics.html&quot;&gt;Linguistics in Vespa | Vespa. Big data. Real time. Open source.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Dockerコンテナ上でJavaで書いたコードを動かすまで</title><link>https://mem-archive.com/posts/2023/01/03/post-2988/</link><guid isPermaLink="true">https://mem-archive.com/posts/2023/01/03/post-2988/</guid><pubDate>Tue, 03 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;前回、Dockerを最低限動かす準備をしました。今回は、Docker上でJavaで書いたコードを動かすまでをやってみます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2023/01/02/post-2967/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Javaのコードを用意&lt;/h2&gt;
&lt;p&gt;まずはDocker上で動かすためのJavaのコードを用意します。今回は単純にHello Worldを出力するだけの以下のコードを使います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class HelloWorld {
    public static void main(String args[]) {
        System.out.println(&quot;Hello World!!&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このJavaファイルを、Dockerfileを作成予定のディレクトリと同じディレクトリに置いておきます。&lt;/p&gt;
&lt;h2&gt;Dockerfileの作成&lt;/h2&gt;
&lt;h3&gt;ベースイメージの選択&lt;/h3&gt;
&lt;p&gt;ベースイメージには無難にopenjdkあたりを選べばいいかと思ってましたが、以下を見るとopenjdkのイメージはdeprecatedになっているようでした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/_/openjdk&quot;&gt;openjdk - Official Image | Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なので今回は、openjdkのイメージからの移行先の1つとして紹介されていた以下のeclipse-temurinを利用します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/_/eclipse-temurin&quot;&gt;eclipse-temurin - Official Image | Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Dockerfileの記述&lt;/h3&gt;
&lt;p&gt;Dockerfileを記述します。Dockerfileにはjavaのコンパイルや実行に関する情報も記述します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ベースイメージにはeclipse-temurinを利用
FROM eclipse-temurin:17.0.5_8-jdk

# 作業用のディレクトリを作成
WORKDIR /work

# ローカルのHelloWorld.javaというファイルをDockerコンテナ上の作業用ディレクトリにコピー
COPY ./HelloWorld.java /work

# コンパイル
RUN javac HelloWorld.java

# 実行
CMD java HelloWorld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、「ローカルの&lt;code&gt;HelloWorld.java&lt;/code&gt;というファイルをDocker上にコピーして、コンパイルして実行する」という処理を記述したDockerfileが完成しました。&lt;/p&gt;
&lt;h2&gt;Dockerイメージの作成 &amp;amp; 実行&lt;/h2&gt;
&lt;p&gt;Dockerfileから&lt;code&gt;java17_helloworld&lt;/code&gt;という名前のDockerイメージを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker build -t java17_helloworld .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作成したDockerイメージを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker run java17_helloworld
Hello World!!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事にJavaのコードがコンパイル、実行され、&lt;code&gt;Hello World!!&lt;/code&gt;という文字が出力されました。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://yanomy.hatenablog.com/entry/2019/01/19/161931&quot;&gt;dockerでJavaの動作確認 - yanom blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.jp/engine/userguide/eng-image/dockerfile_best-practice.html#add-copy&quot;&gt;Dockerfile を書くベスト・プラクティス — Docker-docs-ja 17.06.Beta ドキュメント&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>MacでDockerでHello Worldするまで</title><link>https://mem-archive.com/posts/2023/01/02/post-2967/</link><guid isPermaLink="true">https://mem-archive.com/posts/2023/01/02/post-2967/</guid><pubDate>Mon, 02 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今更ながらDockerを使う機会が出てきたので、Hello World的なDokerfileを作って実行するまでの備忘録です。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;p&gt;メイン機はWindowsで、MacはサブPCとして使っているので古めのスペックです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PC : MacBook Air (13-inch, Early 2015)&lt;/li&gt;
&lt;li&gt;OS : macOS Monterey 12.6.2&lt;/li&gt;
&lt;li&gt;CPU : Intel Core i5 (x86)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Docker Desktopのインストール&lt;/h2&gt;
&lt;p&gt;MacにDocker Desktopをインストールしていきます。homebrewでもインストールできるようですが、とりあえずは公式ドキュメントに従ってインストールしていきます。（今回はDocker DesktopのGUIは使いませんが、一緒にDockerの実行環境もインストールできるので）&lt;/p&gt;
&lt;p&gt;私の使っているMacはIntelのCPU搭載のものなので、以下公式ドキュメントの「Mac with Intel chip」ボタンから&lt;code&gt;Docker.dmg&lt;/code&gt;をダウンロード、インストールするだけです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/desktop/install/mac-install/&quot;&gt;Install Docker Desktop on Mac | Docker Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;インストールしたDockerアプリを起動すると利用条件的なことが書いた画面が表示されますが、今回は個人利用が目的で無償利用できる範囲内なので「accept」ボタンを押します。&lt;/p&gt;
&lt;p&gt;これでインストールは完了です。&lt;/p&gt;
&lt;h2&gt;Dokerfileの作成&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;というファイル名のテキストファイルを作成します。とりあえず最小限の以下の内容を記述します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 必須項目。ベースとなるイメージ。今回はubuntuの20.04を選択
FROM ubuntu:20.04

# コンテナ起動後に実行するコマンド
CMD echo &quot;Hello World!!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回はベースのイメージにはubuntuを利用します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.docker.com/_/ubuntu&quot;&gt;ubuntu - Official Image | Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;イメージの作成&lt;/h2&gt;
&lt;p&gt;作成したDokerfileを以下のコマンドでビルドします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# カレントディレクトリのDockerfileからhelloworldという名前のイメージを作成する
$ docker build -t helloworld .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;イメージ一覧を表示して、イメージが作成されていることを確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker images
REPOSITORY          TAG       IMAGE ID       CREATED        SIZE
helloworld          latest    bdc0073740d3   3 weeks ago    72.8MB
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;コンテナの作成&lt;/h2&gt;
&lt;p&gt;作成したイメージからコンテナを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# helloworldというイメージからecho_helloworldというコンテナを作成する
$ docker create --name echo_helloworld helloworld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;コンテナ一覧を表示して、コンテナが作成されていることを確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# -aオプションをつけることで、実行中のコンテナだけでなくすべてのコンテナを表示できる
$ docker container ls -a
CONTAINER ID   IMAGE        COMMAND                   CREATED         STATUS    PORTS     NAMES
7eaa2f8e2ec5   helloworld   &quot;/bin/sh -c &apos;echo \&quot;H…&quot;   8 seconds ago   Created             echo_helloworld
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;コンテナの実行&lt;/h2&gt;
&lt;p&gt;作成したコンテナを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker start -a echo_helloworld
Hello World!!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/etaroid/items/b1024c7d200a75b992fc&quot;&gt;【図解】Dockerの全体像を理解する -前編- #docker-compose - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/container_ls/&quot;&gt;docker container ls | Docker ドキュメント&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/commandline/start/&quot;&gt;docker start | Docker ドキュメント&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Chromeのクロスドメイン制約を一時的に無効にする方法</title><link>https://mem-archive.com/posts/2021/12/21/post-2918/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/12/21/post-2918/</guid><pubDate>Tue, 21 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ローカルでWebサービスとかを開発していると、フロントエンド(FE)とバックエンド(BE)で別々のポートを使いたいことが結構あります。&lt;/p&gt;
&lt;p&gt;ただ、単純に別々のポートを使ってしまうと、クロスドメイン制約に引っかかってしまうため、FEからBEへのAPIコールがブロックされてCORSエラーが出てしまいます。&lt;/p&gt;
&lt;p&gt;Proxyサーバをローカルに立てれば回避できますが、ローカル環境での開発でそこまで面倒なことしたくなかったので、Chromeのクロスドメイン制約を無効化することにしました。&lt;/p&gt;
&lt;p&gt;Chrome全体のクロスドメイン制約を無効化してしまうのはセキュリティ的に流石にあれなので、一時的に無効化した状態でChromeを立ち上げる方法を備忘録として残しておきます。&lt;/p&gt;
&lt;p&gt;なお、動作確認をした環境はWindows10とChromeのバージョン96.0.4664.110になります。&lt;/p&gt;
&lt;h2&gt;発生した問題&lt;/h2&gt;
&lt;p&gt;フロントエンドで4200ポート、バックエンドで8080ポートを利用して何も考えずにChromeで動作させようとすると、developer toolのconsole上にこんな感じでCORSエラーが表示されて、アクセスがブロックされてしまいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Access to XMLHttpRequest at &apos;http://localhost:8080/&apos; from origin &apos;http://localhost:4200&apos; has been blocked by CORS policy: No &apos;Access-Control-Allow-Origin&apos; header is present on the requested resource.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解決方法&lt;/h2&gt;
&lt;p&gt;クロスドメイン制約を一時的に無効にした状態でChromeを立ち上げます。&lt;/p&gt;
&lt;p&gt;コマンドプロンプトを起動して、以下のように叩けばクロスドメイン制約を無効化したChromeのウィンドウを起動できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;chrome.exeのパス&amp;gt; --disable-web-security --user-data-dir=&amp;lt;適当なディレクトリ&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例えば、自分の環境だとこんな感じでコマンドを叩けば無効化したウィンドウを起動できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;C:\Program Files (x86)\Google\Chrome\Application\chrome.exe&quot; --disable-web-security --user-data-dir=&quot;C:\chromeTemp&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なお当然ですが、セキュリティ的な観点で、クロスドメイン制約を無効化したChromeウィンドウでは開発してるWebサービス以外は開かないようにしましょう。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://alfilatov.com/posts/run-chrome-without-cors/&quot;&gt;Run Chrome browser without CORS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>JavaCVを使って開発したアプリのJARファイルサイズを小さくする</title><link>https://mem-archive.com/posts/2021/11/07/post-2838/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/11/07/post-2838/</guid><pubDate>Sun, 07 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;JavaCVを使って画像処理のコードを書きました。作成したアプリをPaaSで動かしたかったので依存関係全部入りのfat JARを作ったのですが、大したアプリではないのにJARのファイルサイズがとても大きくなってしまったので、ファイルサイズの削減を試みます。&lt;/p&gt;
&lt;p&gt;fat JARの作成方法から知りたい場合は↓の記事をご参照ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2021/11/07/post-2869/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;もともとのJARファイルサイズ&lt;/h2&gt;
&lt;p&gt;もともとの&lt;code&gt;build.gradle&lt;/code&gt;には、JavaCVのREADMEにしたがって↓のように依存関係を記述していました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacv&quot;&gt;GitHub - bytedeco/javacv: Java interface to OpenCV, FFmpeg, and more · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;dependencies {
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacv-platform&apos;, version: &apos;1.5.6&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると生成されたJARファイルのサイズは779Mとなり、とても大きいものになりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   779M Nov  7 07:17 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こんなに大きいとPaaSへのデプロイに無駄に時間がかかってあれなので、JARファイルのサイズ削減してみました。&lt;/p&gt;
&lt;h2&gt;JARファイルのサイズ削減&lt;/h2&gt;
&lt;p&gt;今回はOpenCVをJavaで使いたくてJavaCVを利用しました。&lt;/p&gt;
&lt;p&gt;JavaCVはOpenCV以外のライブラリも色々とラップしているので、方針としては使っていないライブラリは極力依存関係に含めないようにしたいと思います。&lt;/p&gt;
&lt;p&gt;結論だけ先に書いておくと、おすすめは方法3です。&lt;/p&gt;
&lt;h3&gt;方法1：不要なライブラリを依存関係から除外するパターン&lt;/h3&gt;
&lt;p&gt;依存関係に&lt;code&gt;javacv-platform&lt;/code&gt;を単純に指定しただけではOpenCV以外の諸々のライブラリも含まれてしまうので、↓のように使っていないライブラリを明示的に指定して、依存関係に追加しないようにしてしまいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dependencies {
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacv-platform&apos;, version: &apos;1.5.6&apos;, {
        exclude module: &apos;ffmpeg-platform&apos;
        exclude module: &apos;flycapture-platform&apos;
        exclude module: &apos;libdc1394-platform&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とりあえず↑の3つだけ除外してJARファイルを作ってみると、ファイルサイズは606Mまで削減できました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   606M Nov  7 07:35 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方法2：必要なライブラリのみ依存関係に追加するパターン&lt;/h3&gt;
&lt;p&gt;頑張って使ってないもの全部除外するのは面倒なので、はじめからOpenCV部分だけ依存関係に追加すればいいんじゃない？と思ってやったのがこれになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dependencies {
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv-platform&apos;, version: &apos;4.5.3-1.5.6&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;↓の&lt;code&gt;pom.xml&lt;/code&gt;を見てjavacv-platformの依存関係を調べて、opencv-platformだけあれば大丈夫そうだなと思ってこの方法試してみて、実際動作的には問題ありませんでした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacv/blob/release/platform/pom.xml&quot;&gt;javacv/platform/pom.xml at release · bytedeco/javacv · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;結果としてはJARファイルのサイズを464Mまで削減することができました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   464M Nov  7 07:40 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方法3：必要なライブラリのみ依存関係に追加するパターン その2&lt;/h3&gt;
&lt;p&gt;方法2でOpenCV以外の依存関係は除外することに成功しました。&lt;/p&gt;
&lt;p&gt;ただ、方法2で依存関係に追加した&lt;code&gt;opencv-platform&lt;/code&gt;はWindows、macOS、Linux、Android、iOSといったあらゆる環境で動作するように、各環境向けのライブラリファイルも含まれています。&lt;/p&gt;
&lt;p&gt;今回作ったアプリは&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;開発環境はMacまたはWindows、本番環境はLinux&lt;/li&gt;
&lt;li&gt;開発環境も本番環境も64ビット版OSで動作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といった前提なので、AndroidやiOS、armやx86環境向けの諸々は依存関係に含めなくても良さそうです。&lt;/p&gt;
&lt;p&gt;なので必要な環境向けの依存関係のみ記述してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dependencies {
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;linux-x86_64&apos;
   implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;macosx-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;windows-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;openblas-platform&apos;, version: &apos;0.3.17-1.5.6&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは&lt;code&gt;opencv-platform&lt;/code&gt;の&lt;code&gt;pom.xml&lt;/code&gt;の依存関係を見て、必要な環境向けの依存関係だけ抜き出してきたものになります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacpp-presets/blob/release/opencv/platform/pom.xml&quot;&gt;javacpp-presets/opencv/platform/pom.xml at release · bytedeco/javacpp-presets · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上記&lt;code&gt;pom.xml&lt;/code&gt;では&lt;code&gt;classifier&lt;/code&gt;の部分は変数化されていますが、大本はここに定義してあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacpp-presets/blob/release/pom.xml#L58-L86&quot;&gt;javacpp-presets/pom.xml at release · bytedeco/javacpp-presets · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この状態でJARを作ると、204Mまでサイズダウンできました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   204M Nov  7 08:52 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでもともとのファイルサイズの1/4強までサイズを削減することに成功しました。&lt;/p&gt;
&lt;h3&gt;方法4：必要なライブラリのみ依存関係に追加するパターン 番外編&lt;/h3&gt;
&lt;p&gt;基本は方法3と同じで真新しいものはないので番外編としています。&lt;/p&gt;
&lt;p&gt;更に依存関係を細かく整理して、さらなるサイズ削減を目指してみます。あまり細かくすると新しい環境に対応したい場合の保守コストが上がるので、個人的には方法3くらいまでで妥協できるならしておくのがいいと思います。&lt;/p&gt;
&lt;p&gt;さて、方法3で依存関係に&lt;code&gt;openblas-platform&lt;/code&gt;を含めていましたが、実はこれも各環境向けのライブラリファイルが含まれています。&lt;code&gt;openblas-platform&lt;/code&gt;の依存に入っている&lt;code&gt;javacpp-platform&lt;/code&gt;も各環境向けのライブラリが存在します。&lt;/p&gt;
&lt;p&gt;といったように、依存関係を下れるだけ下って必要最低限のものだけ追加するようにしてみたら、↓のような感じになりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dependencies {
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;linux-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;macosx-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;opencv&apos;, version: &apos;4.5.3-1.5.6&apos;, classifier: &apos;windows-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;openblas&apos;, version: &apos;0.3.17-1.5.6&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;openblas&apos;, version: &apos;0.3.17-1.5.6&apos;, classifier: &apos;linux-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;openblas&apos;, version: &apos;0.3.17-1.5.6&apos;, classifier: &apos;macosx-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;openblas&apos;, version: &apos;0.3.17-1.5.6&apos;, classifier: &apos;windows-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacpp&apos;, version: &apos;1.5.6&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacpp&apos;, version: &apos;1.5.6&apos;, classifier: &apos;linux-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacpp&apos;, version: &apos;1.5.6&apos;, classifier: &apos;macosx-x86_64&apos;
    implementation group: &apos;org.bytedeco&apos;, name: &apos;javacpp&apos;, version: &apos;1.5.6&apos;, classifier: &apos;windows-x86_64&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでJARファイルを作成すると136Mまでファイルサイズが減りました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   136M Nov  7 09:22 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最終的に当初のJARのファイルサイズの1/5以下までファイルサイズを削減することができました。これでJARファイルの生成とかデプロイがかなり高速化できました。&lt;/p&gt;
&lt;p&gt;蛇足ですが、今回はmacOS、Windows、Linuxの3種類のOSで動作するようにしましたが、これをさらに例えばmacOSだけで動けばいいように依存関係を絞ると40M程度までサイズダウンできます。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacv/issues/654&quot;&gt;Huge APK file size on Android · Issue #654 · bytedeco/javacv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>【Gradle】依存ライブラリをすべて含めたJAR(fat JAR)を作成する</title><link>https://mem-archive.com/posts/2021/11/07/post-2869/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/11/07/post-2869/</guid><pubDate>Sun, 07 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Javaで開発したコードをPaaSとかにデプロイするためには、依存しているライブラリをすべてJARの中に含めたJAR (=far JAR)を作ってそれをデプロイしないと動きません。&lt;/p&gt;
&lt;p&gt;新規にJavaでサービスを作ったのですが、fat JARという概念を知らずにローカルでは動くのにデプロイしたら動かないなーとなってハマってしまったので、fat JARの作り方をメモしておきます。&lt;/p&gt;
&lt;h2&gt;fat JARの作り方&lt;/h2&gt;
&lt;h3&gt;build.gradleの修正&lt;/h3&gt;
&lt;p&gt;まずは&lt;code&gt;build.gradle&lt;/code&gt;を修正します。&lt;code&gt;build.gradle&lt;/code&gt;のもともとの&lt;code&gt;plugins&lt;/code&gt;の設定がこうなっているところに1行追加して&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;plugins {
    id &apos;java&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;↓のようにします。&lt;code&gt;build.gradle&lt;/code&gt;の修正はこれだけです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;plugins {
    id &apos;java&apos;
    id &apos;com.github.johnrengelman.shadow&apos; version &apos;7.+&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;追加したのはshadowというプラグインで、fat JARを簡単に作成するためのGradleプラグインです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gradleup.com/shadow/&quot;&gt;Shadow Gradle Plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なお、使用しているGradleのバージョンによってpluginsに指定するshadowのバージョンを使い分ける必要があります。&lt;/p&gt;
&lt;p&gt;Gradleのそれぞれのバージョンに対応しているshadowのバージョンは次の通りです。自分はGradle 7.1を利用しているので、7系の最新バージョンが入るように&lt;code&gt;7.+&lt;/code&gt;と記述しています。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gradleのバージョン&lt;/th&gt;
&lt;th&gt;shadowのバージョン&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5.x&lt;/td&gt;
&lt;td&gt;5.2.0 - 6.0.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.x&lt;/td&gt;
&lt;td&gt;5.2.0 - 6.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7.x&lt;/td&gt;
&lt;td&gt;7.0.0以上&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最新のバージョンの対応表は下記のshadowのGithubのREADMEを参照してください。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/johnrengelman/shadow&quot;&gt;GitHub - GradleUp/shadow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;fat JARを作る&lt;/h3&gt;
&lt;p&gt;さて、あとはfat JARファイルを作成するだけです。&lt;/p&gt;
&lt;p&gt;IntelliJを使っている場合は次の画像のようにGradleのタスクの中にshadowJarというタスクが現れているはずなので、それを実行すれば依存関係全部入りのfat JARファイルを作成できます。&lt;code&gt;build/libs&lt;/code&gt;以下にJARファイルが生成されているはずです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2021-11-07-at-3.39.04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;IDEでなくコマンドで実行したい場合は以下のコマンドで作成できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./gradlew shadowJar
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;確認&lt;/h3&gt;
&lt;p&gt;最後に、本当に依存関係が含まれたJARができたのか確認してみます。&lt;/p&gt;
&lt;p&gt;まずは生成されたJARファイルの確認です。ファイルサイズが大きいので色々含まれていそうです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -lh build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
-rw-r--r-- 1 user  staff   779M Nov  7 03:49 build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なお今回はJavaCVというどでかいライブラリを使ったのでこんなに大きなJARになりましたが、大体はもっと小さいと思います。&lt;/p&gt;
&lt;p&gt;もうちょっとちゃんと確認したいので、JARファイルの中身を確認してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ jar tf build/libs/javacv_url_image_loader-1.0-SNAPSHOT-all.jar
META-INF/
...
org/bytedeco/javacv/
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事依存ライブラリの情報が作成したJARに含まれていました。&lt;/p&gt;
&lt;p&gt;これでローカル環境以外でも動く依存関係全部入りのfat JARファイルを作成できるようになりました。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/suin/items/641c1c1ec9ab5447221e&quot;&gt;Gradle: 依存ライブラリ入りのjarを作る #gradle - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>URL上の画像をJavaCV (OpenCV)で読み込む</title><link>https://mem-archive.com/posts/2021/11/06/post-2827/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/11/06/post-2827/</guid><pubDate>Sat, 06 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaでOpenCVを使う必要があったので、JavaのラッパーライブラリであるJavaCVを利用してみました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacv&quot;&gt;GitHub - bytedeco/javacv: Java interface to OpenCV, FFmpeg, and more · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;その際に、URL上の画像を読み込んで加工するのに少々ハマったので、その対応メモです。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;URL上の画像パスから画像をMat型で読み込んで、OpenCVで加工したいのです。&lt;/p&gt;
&lt;h2&gt;ハマったポイント&lt;/h2&gt;
&lt;p&gt;URLから画像を読み込む機能はOpenCVにはなさそうだったので、Javaの&lt;code&gt;ImageIO&lt;/code&gt;クラスを使ってURLから画像を読み込んで、それをOpenCVのMatに変換してやろうと思いました。&lt;/p&gt;
&lt;p&gt;当初は&lt;code&gt;ImageIO&lt;/code&gt;クラスで読み込んだ画像データはint配列として取り出せるので、それをバイト配列に変換してからOpenCVの&lt;code&gt;Mat&lt;/code&gt;で読み込めばいけるだろうと思ったんですが、こんな感じでなんだか色味がおかしくなってしまいました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./result.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ちなみに元画像は↓のOpenCVのサンプル画像を利用させてもらってます。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/fruits.jpg&quot;&gt;https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/fruits.jpg&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;どうせBGRとして読み込むべきところをRGBとして読み込んじゃってるとかそんなんだろうと思ってcvtColorで変換かけてみましたが、赤っぽくなるだけで元画像と同じ色味にはなりませんでした。&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;原因は、&lt;code&gt;ImageIO&lt;/code&gt;クラスから取り出したint配列はARGBの順で並んでいるため、その順のまま&lt;code&gt;Mat&lt;/code&gt;に変換してしまうとおかしな色になってしまうことにあります。&lt;/p&gt;
&lt;p&gt;なので、この問題を解決するためには、ARGBの並び順を&lt;code&gt;Mat&lt;/code&gt;で読みこめるBGRAの順に並び変えないといけないです。&lt;/p&gt;
&lt;p&gt;そういった変換には&lt;code&gt;cvtColor&lt;/code&gt;を使いたいところですが、残念ながらOpenCVにはARGBをBGRAなどに変換する処理は実装されていません。&lt;/p&gt;
&lt;p&gt;そのため、ARGBからBGRAに変換する部分は自前で実装する必要があります。&lt;/p&gt;
&lt;h2&gt;実装&lt;/h2&gt;
&lt;p&gt;上記のARGBからBGRAに変換する部分を自前実装して、URLから画像を読み込んでMat型に格納するサンプルコードがこちらになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Sample {
    public static void main(String[] args) throws IOException {
        // URLから画像を取得
        URL url = new URL(&quot;https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/fruits.jpg&quot;);
        BufferedImage bufferedImage = ImageIO.read(url);

        // 各画素のARGB色情報を抽出
        // intは4byteなので、各byteがそれぞれARGBの各要素に対応している
        int[] argbArray = bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), null, 0, bufferedImage.getWidth());

        // int配列をbyte配列に変換する
        // その際、Mat型で読み込めるようにARGBの並びをBGRAに変換する
        byte[] bgraArray = new byte[argbArray.length * 4];
        for (int i = 0; i &amp;lt; argbArray.length; i++) {
            bgraArray[i * 4 + 0] = (byte) ((argbArray[i] &amp;gt;&amp;gt; 0) &amp;amp; 0xFF); // B
            bgraArray[i * 4 + 1] = (byte) ((argbArray[i] &amp;gt;&amp;gt; 8) &amp;amp; 0xFF); // G
            bgraArray[i * 4 + 2] = (byte) ((argbArray[i] &amp;gt;&amp;gt; 16) &amp;amp; 0xFF); // R
            bgraArray[i * 4 + 3] = (byte) ((argbArray[i] &amp;gt;&amp;gt; 24) &amp;amp; 0xFF); // A
        }

        // Mat型として読み込み
        Mat image = new Mat(bufferedImage.getHeight(), bufferedImage.getWidth(), CV_8UC4, new BytePointer(bgraArray));
        imwrite(&quot;result.jpg&quot;, image);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上記コードを実行すると、無事元画像と同じものが&lt;code&gt;result.jpg&lt;/code&gt;として保存されます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./result-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bytedeco/javacv/issues/888#issuecomment-361126796&quot;&gt;How to convert between Mat and byte[] · Issue #888 · bytedeco/javacv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Android/iOSアプリ「英語多読記録簿」をリリースしました</title><link>https://mem-archive.com/posts/2021/06/08/post-2743/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/06/08/post-2743/</guid><pubDate>Tue, 08 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import img1 from &quot;./Screenshot_20210605-070216-513x1024.png&quot;
import img2 from &quot;./Screenshot_20210605-070257-513x1024.png&quot;
import img3 from &quot;./Screenshot_20210522-225326-512x1024.png&quot;
import img4 from &quot;Screenshot_20210609-024204-1-513x1024.png&quot;&lt;/p&gt;
&lt;p&gt;最近アプリ開発周りの勉強をしていて、先日「英語多読記録簿」というアプリをリリースしたためその紹介です。&lt;/p&gt;
&lt;h2&gt;作ったアプリ&lt;/h2&gt;
&lt;p&gt;英語多読記録簿というアプリを作りました。多読の記録を管理するためのアプリです。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;display: flex; align-items: center; gap: 16px; margin-bottom: 12px;&quot;&amp;gt;
&amp;lt;img src=&quot;/images/english_icon.png&quot; alt=&quot;英語多読記録簿&quot; style=&quot;width: 140px; height: auto; border-radius: 18px;&quot; /&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div style=&quot;font-size: 1.2rem; font-weight: bold; line-height: 1.2;&quot;&amp;gt;英語多読記録簿&amp;lt;/div&amp;gt;
&amp;lt;div style=&quot;font-size: 0.85rem; margin-top: 4px; color: #555;&quot;&amp;gt;Naoyuki Maeda&amp;lt;/div&amp;gt;
&amp;lt;div style=&quot;display: flex; gap: 12px; align-items: center;&quot;&amp;gt;
&amp;lt;a href=&quot;https://play.google.com/store/apps/details?id=com.memarchive.readingrecorder&quot; target=&quot;_blank&quot; style=&quot;display: inline-block;&quot;&amp;gt;
&amp;lt;img src=&quot;/images/GetItOnGooglePlay_Badge_Print_color_Japanese.svg&quot; alt=&quot;Google Playで手に入れよう&quot; style=&quot;height: 34px; width: auto; pointer-events: none; vertical-align: middle;&quot; /&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://apps.apple.com/jp/app/%E8%8B%B1%E8%AA%9E%E5%A4%9A%E8%AA%AD%E8%A8%98%E9%8C%B2%E7%B0%BF/id1570141184?itscg=30200&amp;amp;itsct=apps_box_badge&amp;amp;mttnsubad=1570141184&quot; target=&quot;_blank&quot; style=&quot;display: inline-block;&quot;&amp;gt;
&amp;lt;img src=&quot;https://toolbox.marketingtools.apple.com/api/v2/badges/download-on-the-app-store/black/ja-jp?releaseDate=1622937600&quot; alt=&quot;App Storeでダウンロード&quot; style=&quot;height: 34px; width: auto; pointer-events: none; vertical-align: middle;&quot; /&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;作ろうと思った背景&lt;/h2&gt;
&lt;p&gt;端的に言うと、多読の記録をするためにちょうどいい手軽なアプリが見つからなかったからです。&lt;/p&gt;
&lt;p&gt;いろいろあって英語をちゃんと勉強しようと思い立って、前々から興味のあった多読をやってみようと思いました。&lt;/p&gt;
&lt;p&gt;ただ、累計でどのくらいの語数を読んだのかを記録するちょうどいいアプリがPlay StoreにもApple Storeにも見つかりませんでした。&lt;/p&gt;
&lt;p&gt;流石にExcelで記録していくのは嫌だったので、まずはアプリを作るところから始めよう、という考えになりました。アプリ開発にも前から興味ありましたし、やってみるかーと一念発起しました。&lt;/p&gt;
&lt;h2&gt;英語多読記録簿の機能紹介&lt;/h2&gt;
&lt;h3&gt;読んだ本の記録&lt;/h3&gt;
&lt;p&gt;はい、当然ですが読んだ本とその語数を記録することができます。&lt;/p&gt;
&lt;p&gt;入力項目は本のタイトルと語数、読み終わった日です。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;本の記録&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ただ、都度自分で本のタイトルとか語数を入力するのは面倒です。なので、多読の定番の本はリストから選択できるようにしています。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;本の記録&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;今まで読んだ本の累計語数と目標達成率の可視化&lt;/h3&gt;
&lt;p&gt;今までに読んだ本の語数の累計をホーム画面で確認できます。&lt;/p&gt;
&lt;p&gt;また、目標の語数を設定しておくと、今までの累計語数が目標の何パーセントに達しているかを一目で確認できます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;本の記録&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;過去に読んだ語数をグラフで確認&lt;/h3&gt;
&lt;p&gt;過去にどれくらいの語数を読んだのかを、時系列グラフで直感的に確認できます。&lt;/p&gt;
&lt;p&gt;多読を続けていくと徐々に読める語数が増えるので、グラフが右肩上がりになるとテンション上がりますね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;本の記録&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;今後の開発予定&lt;/h2&gt;
&lt;p&gt;途中で飽きて開発を投げ出しちゃわないように、現在は最低限の機能に絞って開発してリリースした状態です。&lt;/p&gt;
&lt;p&gt;無事リリースする段階まではこぎつけたので、これからどんどん機能拡張していく予定です。&lt;/p&gt;
&lt;p&gt;今後リリース予定の機能を紹介します。&lt;/p&gt;
&lt;h3&gt;データの引継ぎ機能&lt;/h3&gt;
&lt;p&gt;英語多読記録簿にはデータの引継ぎがないので、今のままだと機種変更時にデータの移行ができません。&lt;/p&gt;
&lt;p&gt;多読は長期間続けることに意味があって、その期間に機種変更することもあると思うので、機種変更時にデータの移行ができる機能を開発予定です。&lt;/p&gt;
&lt;h3&gt;定番の本の拡充&lt;/h3&gt;
&lt;p&gt;定番の本では今のところ「ラダーシリーズ」と「ステップラダーシリーズ」の本しか選択できないため、リストから選べる本をさらに追加する予定です。&lt;/p&gt;
&lt;h3&gt;SNSへの共有機能&lt;/h3&gt;
&lt;p&gt;Twitterとかに自分の多読状況を投稿できた方がモチベーションが上がりそうなので、共有機能を開発予定です。&lt;/p&gt;
&lt;p&gt;→ バージョン1.3.0で実装しました&lt;/p&gt;
&lt;h2&gt;最後に&lt;/h2&gt;
&lt;p&gt;自分のために作ったアプリですが、多読をやってる方、今からやろうとしている方、ぜひ英語多読記録簿アプリを使ってみてください。&lt;/p&gt;
&lt;p&gt;フィードバックを頂けると泣いて喜びます。フィードバックは以下3つのどれかで頂けると見落とす可能性が低いです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Play Store / App Storeでのレビュー&lt;/li&gt;
&lt;li&gt;本ブログ記事のコメント&lt;/li&gt;
&lt;li&gt;TwitterのメンションとかDM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;個人開発が楽しくて肝心の多読の進捗がいまいちなので、多読も頑張るぞ。&lt;/p&gt;
</content:encoded></item><item><title>【Golang】GitHub上のprivateリポジトリをimportする</title><link>https://mem-archive.com/posts/2021/01/01/post-2691/</link><guid isPermaLink="true">https://mem-archive.com/posts/2021/01/01/post-2691/</guid><pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;Go言語で書いたコードをprivateリポジトリに置いてて、そのコードを他のリポジトリから呼び出すためのメモです。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;GitHubのprivateリポジトリにGo言語で書いたライブラリを置いていました。&lt;/p&gt;
&lt;p&gt;このライブラリのリポジトリを、publicに変えることなくprivateのままimportして使いたいのです。&lt;/p&gt;
&lt;h2&gt;単純にimportしようとしたらエラーになる&lt;/h2&gt;
&lt;p&gt;こんな感じでprivateリポジトリのライブラリをimportしようとしたら&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
	&quot;github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib&quot;
)

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こんなエラーが表示されてビルドに失敗しました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build ../src/main.go                                                                                                                                                                                                                                  [master] 
go: finding module for package github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib
../src/main.go:7:2: cannot find module providing package github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib: module github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib: git ls-remote -q origin in /home/user/go/pkg/mod/cache/vcs/588e0f756a53aa1a73372233db9fe1a5389215117dfbe15996c0de76f955b419: exit status 128:
        fatal: could not read Username for &apos;https://github.com&apos;: terminal prompts disabled
Confirm the import path was entered correctly.
If this is a private repository, see https://golang.org/doc/faq#git_https for additional information.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解決方法&lt;/h2&gt;
&lt;p&gt;エラーメッセージの「privateリポジトリの場合はここを見な！」みたいなメッセージとともに表示された以下のリンクを覗いてみます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://golang.org/doc/faq#git_https&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;ここでは解決方法として、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;~/.netrc&lt;/code&gt;にGitHubのアカウント名とAPIのアクセストークンを書いて認証を突破する方法&lt;/li&gt;
&lt;li&gt;GitHubに登録してるsshの鍵を利用して認証を突破する方法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;の2つが紹介されていました。&lt;/p&gt;
&lt;p&gt;今回は後者の方法を試してみます。&lt;br /&gt;
なお、当然ながらこの方法の場合、事前にGitHubに鍵を登録しておく必要があります。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~/.gitconfig&lt;/code&gt;に以下の設定を追加します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[url &quot;ssh://git@github.com/&quot;]
	insteadOf = https://github.com/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで再度ビルドしてみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build ../src/main.go
go: downloading github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib v0.0.0-20201229201758-0ee219d61b2f
../src/main.go:7:2: github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib@v0.0.0-20201229201758-0ee219d61b2f: verifying module: github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib@v0.0.0-20201229201758-0ee219d61b2f: reading https://sum.golang.org/lookup/github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib@v0.0.0-20201229201758-0ee219d61b2f: 410 Gone
        server response:
        not found: github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib@v0.0.0-20201229201758-0ee219d61b2f: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /tmp/gopath/pkg/mod/cache/vcs/588e0f756a53aa1a73372233db9fe1a5389215117dfbe15996c0de76f955b419: exit status 128:
                fatal: could not read Username for &apos;https://github.com&apos;: terminal prompts disabled
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;何やらさっきとは違うエラーが表示されてまたビルドがコケました。&lt;/p&gt;
&lt;p&gt;エラーメッセージでいろいろと調べてみると、どうやらGo言語のバージョン1.3以降は&lt;code&gt;GOPRIVATE&lt;/code&gt;という環境変数を設定する必要がありそうです。&lt;/p&gt;
&lt;p&gt;なので、↓のような感じで、参照したいprivateリポジトリのパスを設定してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOPRIVATE=&quot;github.com/&amp;lt;ユーザ名&amp;gt;/golang-lib&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで無事にビルドが成功しました。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/60579900/how-to-fix-invalid-version-and-could-not-read-username-in-go-get-of-a-priv&quot;&gt;git - How to fix &quot;invalid version&quot; and &quot;could not read Username&quot; in &quot;go get&quot; of a private repository? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Visual StudioのMSB8036エラーの解決方法</title><link>https://mem-archive.com/posts/2020/12/12/post-2658/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/12/12/post-2658/</guid><pubDate>Sat, 12 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Juliusという音声認識ライブラリをビルドしたかったのですが、ビルドしてみたらMSB8036エラーが出てちょっとはまったので、対応方法を記録として残しておきます。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;Visual Studio 2019&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Visual Studio 2019でビルドしたらMSB8036エラーが出た&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;msvc &amp;gt; Julius.sln&lt;/code&gt;がVisual Studio用のソリューションファイルなので、こいつを開いてビルドすれば完了！といけばよかったのですが、以下のエラーがいっぱい出てきました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MSB8036	Windows SDK バージョン 10.0.17134.0 が見つかりませんでした。必要なバージョンの Windows SDK をインストールするか、プロジェクト プロパティ ページで SDK バージョンを変更するか、ソリューションを右クリックして [ソリューションの再ターゲット] を選択してください。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;解決方法&lt;/h2&gt;
&lt;p&gt;必要なWindows SDKのバージョンが足りないよ！！って言われてるみたいなので、インストールしてやればうまく動きそうです。なのでインストールを進めていきます。&lt;/p&gt;
&lt;p&gt;Visual Studioインストール用のexeファイルを実行します。大抵は&lt;code&gt;C:\Program Files (x86)\Microsoft Visual Studio\Installer\setup.exe&lt;/code&gt;にあると思います。&lt;/p&gt;
&lt;p&gt;実行すると以下のような画面が出てくると思うので、「変更」ボタンをクリックします。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mem-archive.com/wp-content/uploads/2020/12/image-3.png&quot;&gt;&lt;img src=&quot;./image-3.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;個別コンポーネント&lt;/code&gt;タブで各種Windows SDKのバージョンを選ぶことができるので、見つからないよ！と怒られてる10.0.17134.0のバージョンを選択してインストールします。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./image-4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;インストール後に再度ビルドを行うとMSB8036エラーは無事解消されました。&lt;/p&gt;
</content:encoded></item><item><title>Google TestのASSERT_EQとEXPECT_EQの違い</title><link>https://mem-archive.com/posts/2020/06/14/post-2562/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/06/14/post-2562/</guid><pubDate>Sun, 14 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;普段C++のユニットテストはGoogle Testを使って書くことが多いのですが、ちょっと時間が空くと諸々仕様忘れて同じこと何回も調べなおしてる気がするので、自分がちょいちょいハマる仕様を順次残していこうかと思います。&lt;/p&gt;
&lt;h2&gt;ASSERT_*とEXPECT_*の違い&lt;/h2&gt;
&lt;p&gt;Google Testでは2つの値を比較する方法に、&lt;code&gt;ASSERT_*&lt;/code&gt;を使う方法と&lt;code&gt;EXPECT_*&lt;/code&gt;を使う方法の2種類あります。この2つの違いをまとめていきます。&lt;/p&gt;
&lt;h3&gt;ASSERT_*&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ASSERT_*&lt;/code&gt;は比較結果が意図しないものの場合、&lt;strong&gt;そのテストはそこで強制終了してしまいます。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文章で説明するより、コメント付きのコードを示した方がわかりやすいので、以下にサンプルコードを貼っておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;gtest/gtest.h&amp;gt;

TEST(SampleTest, assertTest) {
	// ASSERT_*はこけたらそこで強制終了する
	ASSERT_FALSE(true);  // この比較でこけるのでここでこのテストを強制終了する
	ASSERT_TRUE(true);   // ↑で強制終了されるのでこの比較は実行されない
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;つまり、&lt;code&gt;ASSERT_*&lt;/code&gt;を利用してテストが途中でこけると、最後までテスト内容を実行しません。&lt;/p&gt;
&lt;h3&gt;EXPECT_*&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;EXPECT_*&lt;/code&gt;は比較結果が意図しないものでも、&lt;strong&gt;強制終了させずに最後までテストを実行します。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以下、サンプルコードです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;gtest/gtest.h&amp;gt;

TEST(SampleTest, expectTest) {
	// EXPECT_*はこけても最後まで処理は走る
	EXPECT_FALSE(true);  // この比較はこけるけど強制終了はしない
	EXPECT_TRUE(true);   // ↑で強制終了されないのでこの比較も実行される
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;つまり、&lt;code&gt;EXPECT_*&lt;/code&gt;を使うと基本的には最後までテストを実行します。&lt;/p&gt;
&lt;h3&gt;使い分け&lt;/h3&gt;
&lt;p&gt;2つの違いはわかったものの、では&lt;code&gt;ASSERT_*&lt;/code&gt;と&lt;code&gt;EXPECT_*&lt;/code&gt;のどちらを使うべきなのでしょうか。&lt;/p&gt;
&lt;p&gt;公式ドキュメント的には、通常は&lt;code&gt;EXPECT_*&lt;/code&gt;を使うことが推奨されています。一度のテスト実行で複数のバグを見つけることが可能だからです。&lt;/p&gt;
&lt;p&gt;一方で、ある比較が失敗したときに、後続の処理を行っても意味のないようなときは、&lt;code&gt;ASSERT_*&lt;/code&gt;を使う方が良いようです。&lt;/p&gt;
&lt;p&gt;結局のメリデメを知った上で状況によって使い分けましょうということですね。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google/googletest/blob/main/docs/primer.md&quot;&gt;googletest/docs/primer.md at main · google/googletest · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://testing.googleblog.com/2008/07/tott-expect-vs-assert.html&quot;&gt;Google Testing Blog: TotT: EXPECT vs. ASSERT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>lsyncd 2.1で特定のファイルのみを同期させる</title><link>https://mem-archive.com/posts/2020/03/09/post-2461/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/03/09/post-2461/</guid><pubDate>Mon, 09 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;lsyncdで特定のファイルだけを同期したかったのですが、lsyncdはバージョン2.1未満と2.1以上で使い方が違ってはまったのでその対応メモです。&lt;/p&gt;
&lt;p&gt;lsyncdの導入方法はこちら。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2020/03/08/post-2467/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;バージョン2.1ではrsyncOpsが廃止された&lt;/h2&gt;
&lt;p&gt;lsyncd 2.0系では、rsyncOpsオプションの--exclude-fromを上手く使うことで、ディレクトリ単位ではなく、特定のファイルのみを同期させることができました。&lt;/p&gt;
&lt;p&gt;しかし、このrsyncOpsオプションはlsyncd 2.1で廃止され、代わりにrsyncオプションに名前を変えました。&lt;/p&gt;
&lt;p&gt;そのため、従来のrsyncOpsを設定するとlsyncdを起動できず、systemctlのstatusを確認すると以下のようなエラーが表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl status lsyncd
...
 3月 09 03:44:11 Virtual-Machine systemd[1]: Starting LSB: lsyncd daemon init script... 
 3月 09 03:44:11 Virtual-Machine lsyncd[12476]:  * Starting synchronization daemon lsyncd
 3月 09 03:44:11 Virtual-Machine lsyncd[12476]: Error: error preparing /etc/lsyncd/lsyncd.conf.lua: Parameter &quot;rsyncOps&quot; unknown. (if this is not a typo add it to checkgauge)
 3月 09 03:44:11 Virtual-Machine lsyncd[12476]:    ...fail!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/axkibe/lsyncd/blob/master/ChangeLog#L116&quot;&gt;lsyncd/ChangeLog at master · lsyncd/lsyncd · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;バージョン2.1では代わりに_extraオプションを使う&lt;/h2&gt;
&lt;p&gt;lsyncd 2.1系ではrsyncオプション内の_extraオプションを使って特定のファイルのみを同期させることができます。&lt;/p&gt;
&lt;p&gt;この_extraオプション内に&lt;code&gt;--include&lt;/code&gt;と&lt;code&gt;--exclude&lt;/code&gt;を指定してやることで、同期対象のファイル、同期対象外のファイルを設定することができます。&lt;/p&gt;
&lt;p&gt;ですので、以下のように書くと&lt;code&gt;hoge.txt&lt;/code&gt;のみが同期され、その他のファイルは同期されなくなり、特定のファイルのみの同期を実現できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /etc/lsyncd/lsyncd.conf.lua
settings {
        logfile    = &quot;/tmp/lsyncd.log&quot;,
        statusFile = &quot;/tmp/lsyncd.status&quot;
}
sync {
        default.rsync,
        source    = &quot;/home/user/lsyncd_test/src/&quot;,
        target    = &quot;/home/user/lsyncd_test/dst/&quot;,
        rsync     = {
          _extra    = {
            &quot;--include=hoge.txt&quot;,
            &quot;--exclude=*&quot;
          }
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/22375500/is-it-possible-to-sync-just-one-file-with-lsyncd/49942887&quot;&gt;macos - Is it possible to sync just ONE file with lsyncd? - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Ubuntuにlsyncdを導入する</title><link>https://mem-archive.com/posts/2020/03/08/post-2467/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/03/08/post-2467/</guid><pubDate>Sun, 08 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ディレクトリのリアルタイム同期をしたかったのでUbuntu 18.04 LTSにlsyncdを導入しました。その時の手順メモです。&lt;/p&gt;
&lt;h2&gt;lsyncdのインストール&lt;/h2&gt;
&lt;p&gt;まずはlsyncdをインストールします。これはaptコマンド一発で入ります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt install lsyncd
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;設定ファイルを作成&lt;/h2&gt;
&lt;p&gt;同期周りの設定ファイルを作成します。デフォルトだと設定ファイルのパスはは&lt;code&gt;/etc/lsyncd/lsyncd.conf.lua&lt;/code&gt;になっているため、この名前のファイルを作成します。&lt;/p&gt;
&lt;p&gt;もし設定ファイル名やファイルパスを変えたい場合は、&lt;code&gt;/etc/init.d/lsyncd&lt;/code&gt;の以下の部分を修正すればファイル名、ファイルパスを変えることができます多分(やったことはないです)。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CONFIG=/etc/lsyncd/lsyncd.conf.lua
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定ファイルはとりあえず最低限の項目のみを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo mkdir /etc/lsyncd

$ sudo vim /etc/lsyncd/lsyncd.conf.lua
settings {
        logfile    = &quot;/tmp/lsyncd.log&quot;,
        statusFile = &quot;/tmp/lsyncd.status&quot;
}
sync {
        default.rsync,
        source    = &quot;/home/user/lsyncd_test/src/&quot;,
        target    = &quot;/home/user/lsyncd_test/dst/&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;lsyncdの起動&lt;/h2&gt;
&lt;p&gt;lsyncdのプロセスを起動します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl start lsyncd
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;ログの確認&lt;/h2&gt;
&lt;p&gt;lsyncdのログを確認してみます。以下のようなログが出ていれば同期に成功しています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /tmp/lsyncd.log
Sun Mar  8 05:57:38 2020 Normal: recursive startup rsync: /home/user/lsyncd_test/src/ -&amp;gt; /home/user/lsyncd_test/dst/
Sun Mar  8 05:57:38 2020 Normal: Startup of &quot;/home/user/lsyncd_test/src/&quot; finished.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;psコマンドでプロセスが起動してるかも確認してみます。以下のようなプロセスがあれば無事起動しています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ps aux | grep lsyncd | grep -v grep
root     12600  0.0  0.0  12932  2072 ?        SNs  03:53   0:00 /usr/bin/lsyncd -pidfile /var/run/lsyncd.pid /etc/lsyncd/lsyncd.conf.lua
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;もし起動してない場合は&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl status lsyncd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;でlsyncdのステータスを確認してみましょう。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.server-world.info/query?os=Ubuntu_18.04&amp;amp;p=lsync&quot;&gt;Ubuntu 18.04 : Lsync の設定 : Server World&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>【MySQL】新規DBを作成してデータを登録するまで</title><link>https://mem-archive.com/posts/2020/02/25/post-2429/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/02/25/post-2429/</guid><pubDate>Tue, 25 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;普段MySQLをあまり弄らないので、たまに弄ろうとすると毎回各種コマンド忘れて無駄に時間取られるので、新規DBを作成してそこにデータを挿入するまでの手順をメモしておきます。&lt;/p&gt;
&lt;h2&gt;MySQLへ接続&lt;/h2&gt;
&lt;p&gt;何はともあれまずはMySQLに接続します。これでrootユーザとして接続できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mysql -u root -p
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;新規DBの作成&lt;/h2&gt;
&lt;h3&gt;DB作成前の状態&lt;/h3&gt;
&lt;p&gt;DB作成前の状態を確認しておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4つのDBが存在しているようです。&lt;/p&gt;
&lt;h3&gt;DBの新規作成&lt;/h3&gt;
&lt;p&gt;新規DBを作成します。以下のコマンドで任意の名前のDBを作成することができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; create database &amp;lt;DB名&amp;gt;;
Query OK, 1 row affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DB一覧の確認&lt;/h3&gt;
&lt;p&gt;ちゃんとDBが作成されているかどうか確認してみます。DB一覧を表示して、先ほど作成したDBの名前が表示されたら問題なく作成されています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| &amp;lt;DB名&amp;gt;             | &amp;lt;- 作成したDBが存在することを確認
| sys                |
+--------------------+
5 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;新規Tableの作成&lt;/h2&gt;
&lt;p&gt;作成したDBに新しいTableを追加してみます。&lt;/p&gt;
&lt;h3&gt;Table作成前の状態&lt;/h3&gt;
&lt;p&gt;まずは作成前にDBのTableを確認しておきます。当然ながら、DBを作成したばかりなのでまだTableは存在しません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show tables from &amp;lt;DB名&amp;gt;;
Empty set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;新規Table作成&lt;/h3&gt;
&lt;p&gt;新規Tableを作成します。作成は以下のコマンドで行えます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; create table &amp;lt;DB名&amp;gt;.&amp;lt;Table名&amp;gt; (&amp;lt;カラム名1&amp;gt; &amp;lt;型&amp;gt;, &amp;lt;カラム名2&amp;gt; &amp;lt;型&amp;gt;, ... );
Query OK, 0 rows affected (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Table一覧の確認&lt;/h3&gt;
&lt;p&gt;もう一度Table一覧を確認してみます。作成したTable名が表示されると思います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show tables from &amp;lt;DB名&amp;gt;;
+----------------------+
| Tables_in_&amp;lt;DB名&amp;gt;     |
+----------------------+
| &amp;lt;Table名&amp;gt;            | &amp;lt;- 作成したTableが存在することを確認
+----------------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;カラムの確認&lt;/h3&gt;
&lt;p&gt;作成したTableのカラム名も意図したものになっているかどうか確認してみます。確認は以下のコマンドで行います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show columns from &amp;lt;DB名&amp;gt;.&amp;lt;Table名&amp;gt;;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| &amp;lt;カラム名1&amp;gt;  | &amp;lt;型1&amp;gt;        | YES  |     | NULL    |       |
| &amp;lt;カラム名2&amp;gt;  | &amp;lt;型2&amp;gt;        | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;データのinsert&lt;/h2&gt;
&lt;p&gt;それでは、作成したTableにデータを挿入してみます。挿入は、以下のコマンドで行うことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; insert into &amp;lt;Table名&amp;gt; (&amp;lt;カラム名1&amp;gt;, &amp;lt;カラム名2&amp;gt;, ... ) values ( &amp;lt;値1&amp;gt;, &amp;lt;値2&amp;gt;, ...)
Query OK, 1 row affected (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なお、すべてのカラムにデータ入れる場合は、カラム名を省略することができます。その場合、カラム名を省略したこのコマンドでも同じ意味になります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; insert into &amp;lt;Table名&amp;gt; values (&amp;lt;値1&amp;gt;, &amp;lt;値2&amp;gt;, ...)
Query OK, 1 row affected (0.01 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;insertしたデータの確認は以下のコマンドで行います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from &amp;lt;DB名&amp;gt;.&amp;lt;Table名&amp;gt;;
+-----------+--------------+
|&amp;lt;カラム名1&amp;gt;| &amp;lt;カラム名2&amp;gt;  |
+-----------+--------------+
| &amp;lt;値1&amp;gt;     | &amp;lt;値2&amp;gt;        |
+-----------+--------------+
1 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事にデータが挿入できていることを確認できたと思います。&lt;/p&gt;
</content:encoded></item><item><title>【Golang】Echo v4をインストールする</title><link>https://mem-archive.com/posts/2020/02/24/post-2396/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/02/24/post-2396/</guid><pubDate>Mon, 24 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Go言語のWebフレームワーク、Echoを使ってみようと思ったのですが、導入に少しはまってしまったので対応メモです。なお環境は&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu 18.04 LTS (WLS)&lt;/li&gt;
&lt;li&gt;Golang 1.13.4&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;となっています。&lt;/p&gt;
&lt;h2&gt;問題&lt;/h2&gt;
&lt;p&gt;Echoをインストールします。このGithubのREADMEのInstallationに従ってインストールしようとしてみます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/labstack/echo&quot;&gt;GitHub - labstack/echo: High performance, minimalist Go web framework · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;go get&lt;/code&gt;しようとするとこんなエラーが表示されてしまいました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go get github.com/labstack/echo/v4
package github.com/labstack/echo/v4: cannot find package &quot;github.com/labstack/echo/v4&quot; in any of:
        /usr/local/go/src/github.com/labstack/echo/v4 (from $GOROOT)
        /home/username/go/src/github.com/labstack/echo/v4 (from $GOPATH)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;READMEじゃなくてこちらのQuick Startに従って、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://echo.labstack.com/guide/quickstart/&quot;&gt;Quickstart | Echo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下のように実行すると問題なく動きましたが、この書き方だと&lt;strong&gt;最新のv4じゃなくてv3がインストールされる&lt;/strong&gt;みたいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go get -u github.com/labstack/echo/...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解決策&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;$GOPATH&lt;/code&gt;じゃなく&lt;code&gt;てGo Modules&lt;/code&gt;の仕組みを使用するとうまくv4をインストールできる&lt;/strong&gt;ようです。Go ModulesはGo言語 1.11から導入されたみたいなので、このバージョン以上のGo言語が必要になるっぽいです。&lt;/p&gt;
&lt;p&gt;なので、まずは&lt;code&gt;$GOPATH&lt;/code&gt;じゃなくてGo Modulesを使用するように設定します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ export GO111MODULE=on 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そして&lt;code&gt;go mod init&lt;/code&gt;のコマンドを叩きます。&lt;code&gt;example.com&lt;/code&gt;と&lt;code&gt;golang_echo_sample&lt;/code&gt;の部分は好きなように変えてください。&lt;code&gt;golang_echo_sample&lt;/code&gt;の部分がビルドしたときの実行ファイルのデフォルトの名前になります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go mod init example.com/golang_echo_sample
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとは&lt;code&gt;Echo&lt;/code&gt;をimportしたソースコードを置いて、&lt;code&gt;go build&lt;/code&gt;を叩けば自動的にEchoのversion 4をダウンロードして使用してくれます。今回はREADMEにあるHelloWorldのサンプルコードを使用します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
  &quot;net/http&quot;
  &quot;github.com/labstack/echo/v4&quot; // &amp;lt;- Echo v4をimportしている
  &quot;github.com/labstack/echo/v4/middleware&quot;
)

func main() {
  // Echo instance
  e := echo.New()

  // Middleware
  e.Use(middleware.Logger())
  e.Use(middleware.Recover())

  // Routes
  e.GET(&quot;/&quot;, hello)

  // Start server
  e.Logger.Fatal(e.Start(&quot;:1323&quot;))
}

// Handler
func hello(c echo.Context) error {
  return c.String(http.StatusOK, &quot;Hello, World!&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでbuildすると、自動的に必要な依存関係をダウンロードしてくれます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./golang_echo_sample
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;もう一つターミナルを起動させて、curlで動作確認してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl &quot;http://localhost:1323&quot;
Hello, World!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事動きました。初めてGo Modules使ってみたけど楽ですねこれ。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/labstack/echo/issues/1374&quot;&gt;Unable to install echo · Issue #1374 · labstack/echo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Linuxのデフォルトシェルを確認する</title><link>https://mem-archive.com/posts/2020/02/20/post-2383/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/02/20/post-2383/</guid><pubDate>Wed, 19 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;久しぶりにログインしたLinuxサーバで、デフォルトで使用しているシェル何だっけ？となった上に確認方法を忘れていたのでメモです。&lt;/p&gt;
&lt;h2&gt;確認方法&lt;/h2&gt;
&lt;p&gt;これで確認できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo $SHELL
/bin/bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.itmedia.co.jp/help/tips/linux/l0358.html&quot;&gt;現在使用されているシェルが知りたい - ITmedia エンタープライズ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;デフォルトシェルの変更方法&lt;/h2&gt;
&lt;p&gt;デフォルトのシェルを変更したい場合はこちらをご覧ください。自分のおすすめはzshです。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/11/18/post-1163/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>【シェルスクリプト】$&apos;&apos;: command not foundと表示される原因と対処法</title><link>https://mem-archive.com/posts/2020/02/09/post-2356/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/02/09/post-2356/</guid><pubDate>Sun, 09 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;定期的に同じトラップにはまるので、いい加減忘れないよう備忘録です。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;こんな感じで絶対に動きそうなシェルスクリプトがあります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

echo &quot;hogehoge&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;でも実際に実行してみると以下のようなよくわからないエラーが表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bash hoge.sh
hoge.sh: line 2: $&apos;\r&apos;: command not found
hogehoge

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2行目が原因のエラーらしいですが、そもそも2行目は空行なんですが・・・&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;改行コードがLinuxの&lt;code&gt;\n&lt;/code&gt;ではなく、Windowsの&lt;code&gt;\r\n&lt;/code&gt;になっていることが原因です。&lt;/p&gt;
&lt;p&gt;Linuxだと&lt;code&gt;\n&lt;/code&gt;単体で改行コード認定されてしまうので、&lt;code&gt;\r&lt;/code&gt;はコマンドとみなされちゃってエラーになるみたいです。&lt;/p&gt;
&lt;h2&gt;対処法&lt;/h2&gt;
&lt;p&gt;改行コードを&lt;code&gt;\r\n&lt;/code&gt;から&lt;code&gt;\n&lt;/code&gt;に変換すれば無事実行できるようになります。変換はこのコマンドで実行できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dos2unix hoge.sh

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このコマンドのインストール方法やそもそもの改行コードの確認法は以下の記事を参考にしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/23/post-1308/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;変更後はエラーなくシェルスクリプトの実行できました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ bash hoge.sh 
hogehoge

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WindowsとMacの開発者が混在しているような環境だとこういう現象稀によくあって、しかも目に見えるコードは問題ないので対処法知らないと地味に時間かかったりして地味に困る。。。&lt;/p&gt;
&lt;p&gt;なお、シェルスクリプトだけでなくシェルスクリプトで読み込むファイルとかも改行コードが違うと動かないことがあるので気をつけましょう。&lt;/p&gt;
</content:encoded></item><item><title>vimでファイル末尾の改行コードを保存しないようにする</title><link>https://mem-archive.com/posts/2020/01/19/post-2321/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/01/19/post-2321/</guid><pubDate>Sun, 19 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;vimで保存すると末尾に改行コードが入る&lt;/h2&gt;
&lt;p&gt;Linuxのvimだと、デフォルトでファイル末尾に改行コードが挿入されるようになってます。&lt;/p&gt;
&lt;p&gt;なので、catで見たら1行しかないように見えるようなファイルでも、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat hoge.txt 
hogehoge

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実際には末尾に改行コードの&lt;code&gt;\n&lt;/code&gt;が入っています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ od -c hoge.txt 
0000000   h   o   g   e   h   o   g   e  \n
0000011

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;改行コードが入らないように設定する&lt;/h2&gt;
&lt;p&gt;vimにこの設定を入れれば、ファイル末尾の改行コードが入らないようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:set binary
:set noeol

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この設定を入れてファイルを保存すると、ファイル末尾の改行コードが入らないようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ od -c hoge.txt
0000000   h   o   g   e   h   o   g   e
0000010

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>rsyncのappendオプションのせいでファイルが壊れたり転送されない件について</title><link>https://mem-archive.com/posts/2020/01/19/post-2264/</link><guid isPermaLink="true">https://mem-archive.com/posts/2020/01/19/post-2264/</guid><pubDate>Sat, 18 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;データのバックアップにrsyncを使ってみたのですが、appendオプションをつけたせいでファイルが壊れたり転送されなかったりしてはまったので、もうはまらないように備忘録を残しておきます。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;rsyncでファイルの同期をとろうと思ったのですが、一部ファイルが転送されなかったり壊れたりしました。&lt;/p&gt;
&lt;h3&gt;差分があるのにファイルが更新されないパターン&lt;/h3&gt;
&lt;p&gt;まずは差分があるのにファイルが更新されないパターンです。以下のようなファイルがあって、&lt;code&gt;src/&lt;/code&gt;以下のファイルを&lt;code&gt;dst/&lt;/code&gt;に転送したいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat src/hoge.txt 
hogehoge

$ cat dst/hoge.txt 
fugafuga

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この場合、append付きのrsyncのコマンドは以下のようになります。期待としては、&lt;code&gt;src/hoge.txt&lt;/code&gt;と&lt;code&gt;dst/hoge.txt&lt;/code&gt;が同じ内容になっていてほしいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rsync -a --append src/ dst/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行してみました。&lt;code&gt;dst/hoge.txt&lt;/code&gt;の内容は更新されませんでした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat dst/hoge.txt 
fugafuga

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;いろいろ試したところ転送前の状態が、&lt;code&gt;src/hoge.txtのファイルサイズ&lt;/code&gt;≦&lt;code&gt;dst/hoge.txtのファイルサイズ&lt;/code&gt;の場合、差分があっても更新されないみたいです。&lt;/p&gt;
&lt;h3&gt;そもそもファイルが転送されないパターン&lt;/h3&gt;
&lt;p&gt;差分バックアップをしたかったので、上のコマンドに&lt;code&gt;link-dest&lt;/code&gt;オプションも追加してみました。ファイルは以下のようになっています。&lt;code&gt;link-dest&lt;/code&gt;には&lt;code&gt;dst/&lt;/code&gt;を指定します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat src/hoge.txt 
hogehoge

$ cat dst/hoge.txt 
fugafuga

// 何も表示されない
$ ls dst2/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この場合、rsyncのコマンドは以下のようになります。期待としては、新しく&lt;code&gt;dst2/hoge.txt&lt;/code&gt;が作成されていてほしいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rsync -a --append --link-dest=`pwd`/dst/ src/ dst2/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行してみました。&lt;code&gt;dst2/&lt;/code&gt;以下には何も転送されていません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 何も表示されない
$ ls dst2/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先ほど同様、転送前の状態が&lt;code&gt;src/hoge.txtのファイルサイズ&lt;/code&gt;≦&lt;code&gt;dst/hoge.txtのファイルサイズ&lt;/code&gt;の場合、差分があっても更新されないみたいです。&lt;/p&gt;
&lt;h3&gt;ファイルが壊れるパターン&lt;/h3&gt;
&lt;p&gt;次はappendオプションをつけてファイルが壊れるパターンです。以下のようなファイルがあります。これをappendオプションをつけてrsyncで転送してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat src/hoge.txt
hogeeeeeee

$ cat dst/hoge.txt 
fugafuga

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;rsyncコマンドは以下です。&lt;code&gt;src/hoge.txt&lt;/code&gt;の内容が&lt;code&gt;dst/hoge.txt&lt;/code&gt;に反映されていてほしいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rsync -a --append src/ dst/

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行してみました。&lt;code&gt;dst/hoge.txt&lt;/code&gt;が2つのファイルが混ざったようなおかしなことになりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat dst/hoge.txt 
fugafugaee

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;諸々試してみた感じ、転送前の状態で次の2つの条件を満たすとこの事象は発生しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/hoge.txtのファイルサイズ&lt;/code&gt;&amp;lt;&lt;code&gt;dst/hoge.txtのファイルサイズ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/hoge.txt&lt;/code&gt;と&lt;code&gt;dst/hoge.txt&lt;/code&gt;の先頭数バイトの内容が異なる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;rsyncについて調べたときに、途中で中断・再開するためにはappendオプションをつけておけ、みたいな記事を見かけたので何も考えずにつけたのですが、このappendオプションが悪さをしていたみたいです。&lt;/p&gt;
&lt;p&gt;manコマンドでappendオプションの意味を調べてみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ man rsync
...
     --append                append data onto shorter files
...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今まで試したrsyncの挙動と一緒に考えると、&lt;strong&gt;どうやら転送先ファイルの方がサイズが小さい場合に、サイズの違い分の差分のみを転送先ファイルに追加しようとするようです。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;つまり今回のファイルが壊れるケースだと、ファイルサイズの差分分の末尾の&lt;code&gt;ee&lt;/code&gt;だけ転送しようとしたようです。しかし、実際には末尾&lt;code&gt;ee&lt;/code&gt;以外にも差分があったため、実行結果が2種類のファイルの内容が混ざったようなおかしなことになったようです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./rsync-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;なので、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;転送元ファイルよりも転送先ファイルの方がサイズが小さい場合 → ファイルを転送しない&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;転送元ファイルと転送先ファイルにサイズの違い分以上の差分がある場合 → 転送元と転送先ファイル内容が混ざる&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といった状態になるみたいです。&lt;/p&gt;
</content:encoded></item><item><title>Create React Appを使ったChrome拡張開発</title><link>https://mem-archive.com/posts/2019/12/31/post-2218/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/12/31/post-2218/</guid><pubDate>Tue, 31 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;最近趣味でChrome拡張を開発してるのですが、JQueryで大きなhtmlをDOMに挿入するのが辛くなってきました。Reactを使えばそのへん上手く切り分けられて幸せになれるんじゃない？って思ったので、ReactでChrome拡張の作成を試してみました。&lt;/p&gt;
&lt;h2&gt;Create React Appのインストール&lt;/h2&gt;
&lt;p&gt;Create React Appというツールを使うと簡単に新規Reactアプリの開発を始めることができるので、今回はこれを使います。&lt;/p&gt;
&lt;p&gt;Create React Appをまだ導入していない方は次の記事を参考にインストールをしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/01/08/post-1370/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Reactアプリの作成&lt;/h2&gt;
&lt;p&gt;とりあえずデフォルトのReactアプリを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npx create-react-app my-app

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;manifest.jsonの書き換え&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;public/manifest.json&lt;/code&gt;をChrome拡張用に書き換えます。書き換えというか、ほぼ書き直しになります。&lt;/p&gt;
&lt;p&gt;今回はお試しなので、とりあえずミニマムな&lt;code&gt;manifest.json&lt;/code&gt;を用意してやります。&lt;code&gt;public/manifest.json&lt;/code&gt;の中身を一旦全消しして、以下の内容を書き込んでやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Create React App Sample&quot;,
  &quot;version&quot;: &quot;1.0&quot;,
  &quot;manifest_version&quot;: 2,
  &quot;browser_action&quot;: {
    &quot;default_popup&quot;: &quot;index.html&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;各項目が何を意味してるかは下記の公式ドキュメントを御覧ください。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/extensions/manifest&quot;&gt;Manifest file format  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;inline scriptを使用しない設定に修正&lt;/h2&gt;
&lt;p&gt;このままビルドするとJavaScriptのコードがインラインスクリプトとして展開されてしまいます。&lt;/p&gt;
&lt;p&gt;Chrome拡張はインラインスクリプトを使っているとエラーを吐いて正常に動作しないため、htmlファイルからscriptタグでJavaScriptのファイルを読み込むよう設定を入れてやる必要があります。&lt;/p&gt;
&lt;p&gt;この方法は次の記事にまとめていますので、この記事の通りにインラインスクリプトを使わないよう設定してください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/12/31/post-2239/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;ちなみにこの設定を入れずにChrome拡張を動作させようとすると、以下のようなエラーが出力され、拡張が正常に機能しません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Refused to execute inline script because it violates the following Content Security Policy directive: &quot;script-src &apos;self&apos; blob: filesystem:&quot;. Either the &apos;unsafe-inline&apos; keyword, a hash (&apos;sha256-xxx/yyy&apos;), or a nonce (&apos;nonce-...&apos;) is required to enable inline execution.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;manifest.json&lt;/code&gt;に設定を書けばこのエラーは回避できるみたいですが、そもそもセキュリティ的によろしくない、みたいな理由でデフォルトではインラインスクリプトの実行が禁止されてるようなので、インラインスクリプトは使用しないのが無難だと思われます。&lt;/p&gt;
&lt;h2&gt;Chromeに拡張を読み込ませる&lt;/h2&gt;
&lt;p&gt;あとはビルドしてできたファイルをChromeに読み込ませれば、完了です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd my-app
$ npm run build

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでできた&lt;code&gt;build&lt;/code&gt;ディレクトリをChrome拡張として読み込み、アイコンをクリックするとポップアップにReactの画面が表示されます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./Screen-Shot-2019-12-31-at-3.57.27.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/nomuyoshi/items/2ad00e6f5af7fe47eeb5&quot;&gt;create-react-appでChrome拡張機能開発 #JavaScript - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Create React Appでinline scriptを使用しないようにビルドする</title><link>https://mem-archive.com/posts/2019/12/31/post-2239/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/12/31/post-2239/</guid><pubDate>Tue, 31 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;Create React Appで開発したアプリを何も考えずビルドすると、JavaScriptのコードは&lt;code&gt;build/index.html&lt;/code&gt;にインラインスクリプトとして埋め込まれます。&lt;/p&gt;
&lt;p&gt;これを&lt;code&gt;build/index.html&lt;/code&gt;から外部スクリプトとして、各種JavaScriptファイルを読み込むようにしたいです。&lt;/p&gt;
&lt;h2&gt;やり方&lt;/h2&gt;
&lt;p&gt;以下のように&lt;code&gt;my-app&lt;/code&gt;という名前でアプリを作成したとします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npx create-react-app my-app

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;my-app&lt;/code&gt;ディレクトリ直下に&lt;code&gt;**.env**&lt;/code&gt;という名前のファイルを作成します。&lt;/p&gt;
&lt;p&gt;そして&lt;code&gt;.env&lt;/code&gt;に以下を記述します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INLINE_RUNTIME_CHUNK=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで&lt;code&gt;npm run build&lt;/code&gt;を叩くと、JavaScriptのコードはインラインスクリプトではなく外部スクリプトとして読み込まれるようになります。実際に&lt;code&gt;build/index.html&lt;/code&gt;を開いてみると、インラインスクリプトがなくなったことがわかると思います。&lt;/p&gt;
</content:encoded></item><item><title>【Chrome拡張】SPAやSSRのWebページでページ遷移を検知する</title><link>https://mem-archive.com/posts/2019/12/09/post-2164/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/12/09/post-2164/</guid><pubDate>Mon, 09 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;GitHub上で動作するChrome拡張を作成したかったのですが、目的のページへ遷移しても動作しなくてはまったので対応メモです。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;GitHub上の特定のページへ遷移すると発火するChrome拡張を作りたいです。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;manifest.json&lt;/code&gt;の&lt;code&gt;content_scripts&lt;/code&gt;の&lt;code&gt;matches&lt;/code&gt;に当てはまるURLにアクセスしても、&lt;code&gt;content_scripts&lt;/code&gt;に書いたJavaScriptが動作しませんでした。&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;SPAなのかSSRなのかはわかりませんが、GitHubはページ遷移時、ページ全体を読み込むのではなく差分のみを読み込んでレンダリングしているみたいです。&lt;/p&gt;
&lt;p&gt;content scriptだとこの差分更新を上手く検知できないようです。ページの読み込みが発生しない&amp;amp;非同期で差分更新をしてるので当然っちゃ当然なんですが。&lt;/p&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;p&gt;以下の手順を踏むと、SPAとかでもページの遷移を検知できます。&lt;/p&gt;
&lt;h3&gt;イベントページを使う&lt;/h3&gt;
&lt;p&gt;content_scriptsではなく、イベントページ/バックグラウンドページを使います。&lt;/p&gt;
&lt;p&gt;イベントページ/バックグラウンドページを使う場合、&lt;code&gt;manifest.json&lt;/code&gt;に以下のように記述すると、イベントページ/バックグラウンドページとして&lt;code&gt;background.js&lt;/code&gt;が起動します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;background&quot;: {
    &quot;scripts&quot;: [
      &quot;background.js&quot;
    ],
    &quot;persistent&quot;: false
  },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;persistent&lt;/code&gt;はtrueに設定するとバックグラウンドページとして、falseに設定するとイベントページとして動作します。&lt;/p&gt;
&lt;p&gt;バックグラウンドページは常にバックグラウンドで常駐するため、メモリを常に占有してしまいます。イベントページは必要な時のみ立ち上がって、処理が完了したら閉じます。&lt;/p&gt;
&lt;p&gt;現在はイベントページが推奨されているようなので、特別理由がない限りは&lt;code&gt;persistent&lt;/code&gt;はfalseで良さそうです。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/k7a/items/26d7a22233ecdf48fed8&quot;&gt;Chrome拡張の開発方法まとめ　その1：概念編 #JavaScript - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;イベントページでのページ遷移の検知&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;chrome.tabs.onUpdated.addListener&lt;/code&gt;を使うと、うまいことタブの更新を検知できます。&lt;/p&gt;
&lt;p&gt;具体的には以下のような感じで使います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
  if (info.status === &apos;complete&apos; &amp;amp;&amp;amp; tab.url.indexOf(&apos;https://github.com/&apos;) !== -1) {
    // いい感じの処理
  }
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;chrome.tabs&lt;/code&gt;を使うときはmanifest.jsonのpermissionsにtabsを追加する必要があります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;permissions&quot;: [
    &quot;tabs&quot;,
  ],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この&lt;code&gt;chrome.tabs.onUpdated.addListener&lt;/code&gt;ですが、読み込み時と読み込み完了時の2回走ります。&lt;/p&gt;
&lt;p&gt;読み込み時は&lt;code&gt;info.status&lt;/code&gt;が&lt;code&gt;loading&lt;/code&gt;になり、読み込み完了時は&lt;code&gt;info.status&lt;/code&gt;が&lt;code&gt;complete&lt;/code&gt;となります。&lt;/p&gt;
&lt;p&gt;今回は読み込み完了時だけ処理が走ればよく、またGitHub上でのみ動けばいい拡張機能を作りたかったので、上記のようにif分で条件を絞っています。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://var.blog.jp/archives/52749194.html&quot;&gt;Chrome 拡張機能メモ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;イベントページでDOMにアクセスする&lt;/h3&gt;
&lt;p&gt;Chrome拡張では&lt;code&gt;content_scripts&lt;/code&gt;だとDOMに普通にアクセスできるのですが、イベントページやポップアップからDOMへのアクセスしようとすると一工夫必要です。&lt;/p&gt;
&lt;p&gt;具体的には以下のような感じで、&lt;code&gt;chrome.tabs.executeScript&lt;/code&gt;を使用します。&lt;code&gt;executeScript&lt;/code&gt;を使えば、content_scriptsをイベントページに挿入できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
  if (info.status === &apos;complete&apos; &amp;amp;&amp;amp; tab.url.indexOf(&apos;https://github.com/&apos;) !== -1) {
    chrome.tabs.executeScript(null, { file: &apos;./contentScript.js&apos; }, () =&amp;gt; {});
  }
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この例では&lt;code&gt;./contentScript.js&lt;/code&gt;が実行されます。この&lt;code&gt;./contentScripts.js&lt;/code&gt;はcontent_scriptsとして扱われるので普通にDOMにアクセスすることが可能です。&lt;/p&gt;
&lt;p&gt;なお、content_scriptsとして扱われるためmanifest.jsonにURLを登録する必要があります。以下のようにpermissionsに&lt;code&gt;./contentScripts.js&lt;/code&gt;が走る可能性のあるURLを追加しましょう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;permissions&quot;: [
    &quot;tabs&quot;,
    &quot;https://github.com/*&quot;
  ],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここに追加されていないURL上で&lt;code&gt;./contentScripts.js&lt;/code&gt;が発火しようとすると、イベントページのデベロッパーツール上でエラーが発生します。&lt;/p&gt;
&lt;p&gt;これでSPAとかのWebページでも意図したタイミングで発火するChrome拡張を作れそうです。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19758028/chrome-extension-get-dom-content&quot;&gt;javascript - Chrome Extension - Get DOM content - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/extensions/content_scripts&quot;&gt;Manifest - content scripts  |  Chrome Extensions  |  Chrome for Developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>【Linux】mvとcp+rmの違い</title><link>https://mem-archive.com/posts/2019/10/27/post-2077/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/10/27/post-2077/</guid><pubDate>Sun, 27 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ファイルをmvするのと、cpしてから元ファイルをrmで消すのは一見して同じ挙動のように見えます。自分もそう思っていたのですが、微妙に挙動が異なることに気づいたので、違いをまとめます。&lt;/p&gt;
&lt;h2&gt;結論&lt;/h2&gt;
&lt;p&gt;先に結論から書いておくと、mvとcp+rmの違いは、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mvは&lt;strong&gt;inode番号が変わらない&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;cp+rmは&lt;strong&gt;inode番号が変わる&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;です。&lt;/p&gt;
&lt;h2&gt;検証&lt;/h2&gt;
&lt;p&gt;それではinode番号について、mvの場合とcp+rmの場合とで検証してみます。&lt;/p&gt;
&lt;h3&gt;mvの場合&lt;/h3&gt;
&lt;p&gt;ファイルを用意してinode番号を確認します。 inode番号は、lsコマンドのiオプション使えば確認することができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ファイルを作成
$ touch hoge.txt

# inode番号を確認
$ ls -i hoge.txt 
1085901 hoge.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mvでファイルを移動して、移動後のファイルのinode番号を確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mv hoge.txt fuga.txt
$ ls -i fuga.txt
1085901 fuga.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;inode番号が変わっていないことが確認できました。&lt;/p&gt;
&lt;h3&gt;cp + rmの場合&lt;/h3&gt;
&lt;p&gt;inode番号を確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# コピー前のinode番号を確認
$ ls -i fuga.txt
1085901 fuga.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;cpでファイルをコピーして、rmでコピー前のファイルを削除します。その後、コピー後のファイルのinode番号を確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cp fuga.txt piyo.txt
$ rm fuga.txt

$ ls -i piyo.txt 
1084259 piyo.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;inode番号が変わったことを確認できました。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;新しいLinuxの教科書&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/51h3XbUE5EL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B072K1NH76&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B072K1NH76&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/%E6%96%B0%E3%81%97%E3%81%84Linux%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=%E6%96%B0%E3%81%97%E3%81%84Linux%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;3QRe7&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-3QRe7&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>zcatで未圧縮のファイルの中身も表示する</title><link>https://mem-archive.com/posts/2019/10/20/post-2084/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/10/20/post-2084/</guid><pubDate>Sun, 20 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;たまにgzファイルと未圧縮のテキストファイルをまとめて表示したい時があるのですが、毎回オプションを忘れるのでメモです。&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;以下のようにgzファイルとテキストファイルをまとめて表示したいです。&lt;/p&gt;
&lt;p&gt;gzファイルを表示したいときはzcatコマンドを使うものですが、普通に使うと未圧縮のテキストファイルは表示できません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ zcat hoge.txt.gz fuga.txt
hogehoge

gzip: fuga.txt: not in gzip format
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;実現方法&lt;/h2&gt;
&lt;p&gt;zcatにfオプションをつけると、gzファイルもテキストファイルもまとめて表示できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ zcat -f hoge.txt.gz fuga.txt
hogehoge
fugafuga
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;fオプションを付与すれば、gzip形式でもcompress形式でもないファイルに関してはcatと同等の動作をします。なので、gzファイルもテキストファイルもまとめて表示することができます。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;新しいLinuxの教科書&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/51h3XbUE5EL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B072K1NH76&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B072K1NH76&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/%E6%96%B0%E3%81%97%E3%81%84Linux%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=%E6%96%B0%E3%81%97%E3%81%84Linux%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;3QRe7&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-3QRe7&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>homebrewでgccをインストールする</title><link>https://mem-archive.com/posts/2019/08/17/post-2038/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/08/17/post-2038/</guid><pubDate>Sat, 17 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Xcode付属のclangがいまいち使えなかったので、homebrewでgccをインストールすることにしました。その導入手順のメモです。&lt;/p&gt;
&lt;h2&gt;gccのインストール&lt;/h2&gt;
&lt;p&gt;はい、brewコマンド一発でインストール自体は完了します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ brew install gcc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ただ、Xcode付属のclangが既にインストールされていた場合、gccコマンドを叩いてもclangが呼ばれてしまいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Xcode付属のclangが呼ばれる
$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なので、gccコマンドを叩いたときにhomebrewでインストールしたgccが呼ばれるように設定してやる必要があります。&lt;/p&gt;
&lt;h2&gt;homebrewでインストールしたgccが呼ばれるようにする&lt;/h2&gt;
&lt;p&gt;2019年8月現在、homebrewでインストールされるgccはバージョン9系が入るので、gcc/g++という名前でシンボリックリンクを貼ってやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ln -s /usr/local/bin/gcc-9 /usr/local/bin/gcc
$ ln -s /usr/local/bin/g++-9 /usr/local/bin/g++
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、gcc/g++コマンドへのパスが通ります。が、Xcode付属のclangが入っていると、gccという名前の実質clangコンパイラが優先して探索されてしまいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 環境変数のディレクトリ内からgccという名前のコマンドをすべて見つけ出す
$ which -a gcc
/usr/local/bin/gcc &amp;lt;-こっちがhomebrewでインストールしたgcc
/usr/bin/gcc &amp;lt;-こっちはXcode付属のgcc(実質clang)

# gccコマンドを叩いた場合に実行されるコマンド
$ which gcc
/usr/bin/gcc
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ which -a g++
/usr/local/bin/g++
/usr/bin/g++

$ which g++
/usr/bin/g++
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なので、.bash_profileあたりに以下の設定を追加します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=&quot;/usr/local/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に.bash_profileを再読み込みすると、homebrewで導入したgcc/g++が優先して使われるようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ source ~/.bash_profile
$ which gcc
/usr/local/bin/gcc

$ which g++
/usr/local/bin/g++

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/local/Cellar/gcc/9.2.0/libexec/gcc/x86_64-apple-darwin18/9.2.0/lto-wrapper
Target: x86_64-apple-darwin18
Configured with: ../configure --build=x86_64-apple-darwin18 --prefix=/usr/local/Cellar/gcc/9.2.0 --libdir=/usr/local/Cellar/gcc/9.2.0/lib/gcc/9 --disable-nls --enable-checking=release --enable-languages=c,c++,objc,obj-c++,fortran --program-suffix=-9 --with-gmp=/usr/local/opt/gmp --with-mpfr=/usr/local/opt/mpfr --with-mpc=/usr/local/opt/libmpc --with-isl=/usr/local/opt/isl --with-system-zlib --with-pkgversion=&apos;Homebrew GCC 9.2.0&apos; --with-bugurl=https://github.com/Homebrew/homebrew-core/issues --disable-multilib --with-native-system-header-dir=/usr/include --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
Thread model: posix
gcc version 9.2.0 (Homebrew GCC 9.2.0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意点として、環境変数のパスの優先順位を変えたので、&lt;code&gt;/usr/bin&lt;/code&gt;と&lt;code&gt;/usr/local/bin&lt;/code&gt;内に同じ実行ファイルがあった場合、今まで&lt;code&gt;/usr/bin&lt;/code&gt;内の実行ファイルが使われていたのが、&lt;code&gt;/usr/local/bin&lt;/code&gt;内の実行ファイルが使われるようになりますので気をつけましょう。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/usr/local/bin&lt;/code&gt;内のコマンドは基本的にユーザが意図して入れたもののはずなので、困ったらアンインストールしてしまってもいいんじゃないかなと思います。&lt;/p&gt;
</content:encoded></item><item><title>Xcode付属のclangだとC++のfilesystemは動かなかった話</title><link>https://mem-archive.com/posts/2019/08/17/post-2016/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/08/17/post-2016/</guid><pubDate>Sat, 17 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;以前Windowsで書いたC++のコードをMacで動かそうとしたら「filesystemなんてないよ！！」ってclangコンパイラに怒られてちょっと調査したので、その調査メモです。&lt;/p&gt;
&lt;p&gt;先に結論を書いておくと、Xcode付属のclangだとどうやってもfilesystemは使えそうにありませんでした。&lt;/p&gt;
&lt;h2&gt;環境&lt;/h2&gt;
&lt;p&gt;OSはmacOS Mojaveを使用しています。&lt;/p&gt;
&lt;p&gt;clangのバージョンは以下の通りです。Xcode付属のデフォルトのものを使用しています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ clang++ -v
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;余談ですが、Appleはclangのバージョンを独自に振るの、ややこしいからやめてもらえませんかね。どのC++のバージョンに対応しているのかわからない・・・。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;このなんの変哲もない、filesystemをincludeしただけのコードをコンパイルしてみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;filesystem&amp;gt;

int main() {
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ clang++ main.cpp -std=c++17
main.cpp:1:10: fatal error: &apos;filesystem&apos; file not found
#include &amp;lt;filesystem&amp;gt;
         ^~~~~~~~~~~~
1 error generated.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;「filesystemなんかないよ！！」って怒られました。&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;単純に、Xcode付属のclangではまだfilesystemが導入されていない模様です。&lt;/p&gt;
&lt;p&gt;自分の使ってるclang 10.0.1ではC++17のコンパイルオプションは存在するものの、C++17のすべての機能に対応しているわけではないようです。&lt;/p&gt;
&lt;p&gt;実際にinclude用のファイルがあるディレクトリを探してみましたが、filesystemはなさそうです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/filesystem
ls: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/filesystem: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++17で標準入りする前は、filesystemは&lt;code&gt;std::experimental&lt;/code&gt;というnamespaceにいたためexperimentalディレクトリ以下にいないかなーと思いましたが、残念ながらいませんでした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/experimental/filesystem
ls: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/experimental/filesystem: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;というわけで、現状&lt;strong&gt;Xcode付属のclangを使っている限り、&lt;/strong&gt;&lt;code&gt;**std::filesystem**&lt;/code&gt;&lt;strong&gt;は使えません。&lt;/strong&gt;&lt;br /&gt;
(もしどうにかして使えるようにする方法をご存知の方は教えてください)&lt;/p&gt;
&lt;p&gt;どうしてもfilesystemを使いたい場合は次のセクションのいずれかの方法をとる必要がありそうです。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42633477/macos-clang-c17-filesystem-header-not-found&quot;&gt;c++ - macOS Clang C++17 filesystem header not found - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/49577343/filesystem-with-c17-doesnt-work-on-my-mac-os-x-high-sierra/49600206&quot;&gt;c++ - &lt;code&gt;filesystem&lt;/code&gt; with c++17 doesn&apos;t work on my mac os x high sierra - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;h3&gt;その１：Xcode付属のclang以外のコンパイラを導入する&lt;/h3&gt;
&lt;p&gt;Xcode付属のclangを使うのを諦めて、別途clangとかgccとかのコンパイラを用意する方法です。&lt;/p&gt;
&lt;p&gt;homebrewでインストールできるgccの方が導入が簡単なので、こだわりのない人にはgccをおすすめします。gccのインストール方法は以下の記事で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/08/17/post-2038/&quot; /&amp;gt;&lt;/p&gt;
&lt;h3&gt;その2：boostを使用する&lt;/h3&gt;
&lt;p&gt;boostライブラリの中に&lt;code&gt;std::filesystem&lt;/code&gt;と同等の機能の&lt;code&gt;boost::filesystem&lt;/code&gt;があるので、これを使えば解決できるはずです。&lt;br /&gt;
(自分は試したことないです。)&lt;/p&gt;
</content:encoded></item><item><title>ExifからPixel単位の焦点距離を計算する</title><link>https://mem-archive.com/posts/2019/07/28/post-1942/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/07/28/post-1942/</guid><pubDate>Sun, 28 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;3次元復元系の論文を読んでいると、よく「初期値にはExifから取得した焦点距離を使用する」的なことが書いてあります。しかし、カメラ内部パラメータとかの焦点距離はピクセル単位なのに、Exifに記録されてる焦点距離はmm単位です。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/21/post-157/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;このExifの情報を使ってmm単位の焦点距離をピクセル単位に直すことについて言及した日本語ドキュメントってそういえば見たことがないなと思ったので、変換方法を解説しようと思います。&lt;/p&gt;
&lt;h2&gt;使用する情報&lt;/h2&gt;
&lt;p&gt;ピクセル単位の焦点距離導出に使用するExifの情報は以下です。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Exifのタグ名&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;説明&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;focalLength&lt;/td&gt;
&lt;td&gt;mm単位の焦点距離&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;focalPlaneXResolution&lt;/td&gt;
&lt;td&gt;単位長あたりのピクセル数（X軸方向）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;focalPlaneYResolution&lt;/td&gt;
&lt;td&gt;単位長あたりのピクセル数（Y軸方向）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;focalPlaneResolutionUnit&lt;/td&gt;
&lt;td&gt;単位長の種類を示した番号   2: inch   3: cm   4: mm&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;なぜ単位長あたりのピクセル数がX軸とY軸で分かれているかというと、フォトダイオードは完全な正方形ではないからです。詳しくはこちらの記事で解説してます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/25/post-201/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;ピクセル単位の焦点距離の計算方法&lt;/h2&gt;
&lt;p&gt;focalPlaneResolutionUnit=2、すなわち単位長がインチの場合を考えてみると、X軸方向のピクセル単位の焦点距離は以下のように計算できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./exif1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同様にY軸方向のピクセル単位の焦点距離も計算できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./exif2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;なお、focalPlaneXResolutionやfocalPlaneYResolutionがわからない場合でも、Exifに焦点距離（mm単位）と35mmフィルム換算焦点距離（mm単位）の情報があれば、そこからピクセル単位の焦点距離を求めることができます。&lt;br /&gt;
（経験則ですが、focalPlaneXResolutionとかがある場合はそっちを使って計算する方が一般的だと思われます。）&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.exiv2.org/tags.html&quot;&gt;Exiv2 - Image metadata library and tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>権限があるはずなのにfindコマンドでPermission deniedが表示される場合の対処法</title><link>https://mem-archive.com/posts/2019/07/28/post-1976/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/07/28/post-1976/</guid><pubDate>Sun, 28 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;find&lt;/code&gt;コマンドを使ったときに、対象ディレクトリの権限があるはずなのにPermission deniedと表示されて原因究明に時間がかかったため原因と対応メモです。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;以下のように、&lt;code&gt;find&lt;/code&gt;コマンドを叩くと&lt;code&gt;find: Failed to restore initial working directory: /home/user/hoge: Permission denied&lt;/code&gt;といったエラーが表示されてしまいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo -u nobody find ../fuga
../fuga
../fuga/piyo
find: Failed to restore initial working directory: /home/user/hoge: Permission denied
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一応ちゃんとファイル一覧が表示されはしたので、&lt;code&gt;find&lt;/code&gt;コマンド自体は正しく動作してそうです。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;../fuga&lt;/code&gt;の権限を確認してみても、ちゃんとread権限はありそうです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -l ../ | grep fuga
drwxrwxrwx 1 user user       512 Jul 28 03:29 fuga
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一応ファイル一覧自体は取得できているので問題ないといえばないのですが、気持ち悪いので原因の調査を行いました。&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;原因としては、&lt;code&gt;find&lt;/code&gt;コマンドの実行ユーザはカレントディレクトリの実行権限も持っている必要がある、ということのようです。&lt;/p&gt;
&lt;p&gt;確かに、&lt;code&gt;find&lt;/code&gt;コマンドを叩いたディレクトリの権限を見てみると、&lt;code&gt;find&lt;/code&gt;コマンドの実行ユーザは実行権限を持っていませんでした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -l ../ | grep hoge
drwx------ 1 user user       512 Jul 28 03:03 hoge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;read権限を与えてみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 実行ユーザ(nobodyユーザ)でもreadできるようにする
$ sudo chmod 701 ../hoge
$ ls -l ../ | grep hoge
drwx-----x 1 user user       512 Jul 28 03:03 hoge

# 再度nobodyユーザでfindを叩く
$ sudo -u nobody find ../fuga
../fuga
../fuga/piyo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;エラーなく実行できるようになりました。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://hkzo.org/2010/10/find/&quot;&gt;find コマンドはカレントディレクトリへのアクセス権限が必要 | 歩くような速さで・・・&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;p&gt;カレントディレクトリに&lt;code&gt;find&lt;/code&gt;コマンド実行ユーザに実行権限を与えるか、権限を持っているディレクトリで&lt;code&gt;find&lt;/code&gt;コマンドを叩くようにしましょう。&lt;/p&gt;
&lt;p&gt;それか、気にならないなら無視してもまぁいいんじゃないかなと思います。&lt;/p&gt;
</content:encoded></item><item><title>yaml-cpp 0.6.2をVisual Studio 2017以降でビルドする</title><link>https://mem-archive.com/posts/2019/07/14/post-1914/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/07/14/post-1914/</guid><pubDate>Sun, 14 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ちょっとyaml形式でログを吐きたくてyaml-cppを使ってみようと思ったものの、ビルドではまったので対応メモです。&lt;/p&gt;
&lt;h2&gt;まずは普通にビルド&lt;/h2&gt;
&lt;p&gt;yaml-cppはCMakeLists.txtを持っているため、CMakeを使えば簡単にVisual Studioのソリューションファイルを作ることができます。&lt;/p&gt;
&lt;p&gt;ここは特に設定を何も変更せず、デフォルトのままで問題ないです。&lt;/p&gt;
&lt;p&gt;ただ、できたソリューションファイルをそのままビルドしてもコンパイルエラーが大量に発生してしまうので、そいつらに対処してやる必要があります。&lt;/p&gt;
&lt;h2&gt;「 C3646 &apos;_NOEXCEPT&apos;: 不明なオーバーライド指定子です」のエラーの対応&lt;/h2&gt;
&lt;p&gt;さて、ビルドしたらエラーがたくさん出ます。その中で、まずは以下のエラーの対応を行います。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./yaml-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/exceptions.cpp&lt;/code&gt;と&lt;code&gt;include/yaml-cpp/exceptions.h&lt;/code&gt;に&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// This is here for compatibility with older versions of Visual Studio
// which don&apos;t support noexcept
#ifdef _MSC_VER
    #define YAML_CPP_NOEXCEPT _NOEXCEPT
#else
    #define YAML_CPP_NOEXCEPT noexcept
#endif

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;というような記述があります。これを、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// This is here for compatibility with older versions of Visual Studio
// which don&apos;t support noexcept
//#ifdef _MSC_VER
//    #define YAML_CPP_NOEXCEPT _NOEXCEPT
//#else
    #define YAML_CPP_NOEXCEPT noexcept
//#endif

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こうしてしまいましょう。すなわち、1行だけ残してコメントアウトしてやります。&lt;/p&gt;
&lt;p&gt;ちなみにこのエラーですが、&lt;code&gt;_NOEXCEPT&lt;/code&gt;がVisual Studio 2017 version 15.8で削除されたことが原因です。なので、Visual Studio 2017でもこれより古いバージョンであればこのエラーは出ないと思われます。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://devblogs.microsoft.com/cppblog/stl-features-and-fixes-in-vs-2017-15-8/&quot;&gt;STL Features and Fixes in VS 2017 15.8 - C++ Team Blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なお、最新のmasterリポジトリではこの問題は修正されているので、次期バージョンではこの問題は解決されるものと思われます。&lt;/p&gt;
&lt;h2&gt;「C2220 警告をエラーとして扱いました。&apos;object&apos; ファイルは生成されません。」の対応&lt;/h2&gt;
&lt;p&gt;ここまでくればエラーがかなり減っていて、あとはこのエラーだけかと思います。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./yaml2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;構成プロパティ → C/C++ → 全般&lt;/code&gt;の、&lt;code&gt;警告をエラーとして扱う&lt;/code&gt;を&lt;code&gt;いいえ (/WX-)&lt;/code&gt;に設定します。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./yaml3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;対象は以下のプロジェクトファイルです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gmock&lt;/li&gt;
&lt;li&gt;gmock_main&lt;/li&gt;
&lt;li&gt;gtest&lt;/li&gt;
&lt;li&gt;gtest_main&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これで、無事yaml-cpp 0.6.2がVisual Studio 2017でビルドが成功すると思います。&lt;/p&gt;
</content:encoded></item><item><title>【Windows】Portを占有しているプロセスを見つけて強制終了する</title><link>https://mem-archive.com/posts/2019/05/18/post-1870/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/05/18/post-1870/</guid><pubDate>Sat, 18 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近vue.js使ったWebサービス開発の勉強をしてたのですが、ある時&lt;code&gt;npm run serve&lt;/code&gt;を叩いてもlocalhostに繋がらなくなったので、その対応メモです。&lt;/p&gt;
&lt;h2&gt;ポートを占有しているプロセスを見つける&lt;/h2&gt;
&lt;p&gt;まずは、ポートを占有しているプロセスを見つけます。今回は8080番ポートに繋がらなくなったので、このポートを占有しているプロセスを特定します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c:\&amp;gt;netstat -ano | find &quot;:8080&quot;
  TCP         0.0.0.0:8080           0.0.0.0:0              LISTENING       2272
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回の場合ですと、PIDが2272のプロセスが8080番ポートを占有しているようです。&lt;/p&gt;
&lt;p&gt;なお、netstatで使用しているオプションの意味合いはこんな感じです。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;オプション&lt;/th&gt;
&lt;th&gt;説明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;a&lt;/td&gt;
&lt;td&gt;すべてのポートを表示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;n&lt;/td&gt;
&lt;td&gt;名前解決をせずに表示(IPアドレスのまま表示)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;o&lt;/td&gt;
&lt;td&gt;プロセスID(PID)を表示&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;プロセスを確認＆強制終了する&lt;/h2&gt;
&lt;p&gt;PIDからプロセス名の特定は、タスクマネージャーを使えば簡単にできます。&lt;/p&gt;
&lt;p&gt;タスクマネージャーを開いて、&lt;code&gt;詳細&lt;/code&gt;タブを開きPIDからプロセス名を特定します。この場合ですと、PID 2272のプロセスは&lt;code&gt;node&lt;/code&gt;であることがわかります。&lt;/p&gt;
&lt;p&gt;あとは、&lt;code&gt;node&lt;/code&gt;を選択して&lt;code&gt;タスクの終了&lt;/code&gt;をクリックすると、8080番ポートを占有していたプロセスを強制終了できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./taskmanager2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;番外編 ～コマンドでプロセスを確認&amp;amp;強制終了する～&lt;/h2&gt;
&lt;p&gt;タスクマネージャーを使わなくても、コマンドを使用しても同様のことができます。&lt;/p&gt;
&lt;h3&gt;PIDからプロセス名を確認する&lt;/h3&gt;
&lt;p&gt;Windowsには&lt;code&gt;ps&lt;/code&gt;コマンドはありませんが、似たようなコマンドとして、&lt;code&gt;tasklist&lt;/code&gt;コマンドというものがあります。&lt;/p&gt;
&lt;p&gt;このコマンドを使用しても、PIDからプロセス名を特定できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c:\&amp;gt;tasklist | find &quot;2272&quot;
node                          2272 Console                    1    266,048 K
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ポート占有しているプロセスを強制終了する&lt;/h3&gt;
&lt;p&gt;Windowsにも&lt;code&gt;kill&lt;/code&gt;コマンドっぽいコマンドとして&lt;code&gt;taskkill&lt;/code&gt;コマンドがあるので、コマンドからもプロセスを強制終了できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c:\&amp;gt;taskkill /F /PID 2272
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ただ、管理者権限のコマンドプロンプトでこのコマンドを叩いても、こんな感じでエラーが表示されて終了できない場合があったので、タスクマネージャーから強制終了するのが間違いないと思います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c:\&amp;gt;taskkill /F /PID 2272
エラー: PID 2272 のプロセスを強制終了できませんでした。
理由: アクセスが拒否されました。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;アクセスが拒否される原因をご存知の方は教えてください・・・&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/riversun/items/ac70cd4b9cef8140236c&quot;&gt;[TIPS]Windowsで、あるポート番号をListenしているプロセスを調べてkillする #command - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://hironemu.hatenablog.com/entry/20090408/1239163437&quot;&gt;LinuxのpsコマンドみたいなWindowsのコマンド - ありの日記&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>CSSのgrid layoutを使って画面を縦に分割する</title><link>https://mem-archive.com/posts/2019/05/02/post-1827/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/05/02/post-1827/</guid><pubDate>Thu, 02 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;自分は基本的にはバックエンドエンジニアなんですが、GWだし時間あるしフロントエンドも触ってみようかなと思って触ってみました。&lt;/p&gt;
&lt;p&gt;CSSとかほぼ初めて触ったので、初心者が躓くところすべてに躓いてる感あるので、もう躓かないように備忘録がてら記事にしておきます。（なので、初心者丸出しの記事になります）&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;こんな感じで、画面の領域を上下に分割したいのです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./grid_layout.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;実現方法&lt;/h2&gt;
&lt;h3&gt;基本方針&lt;/h3&gt;
&lt;p&gt;表題の通りgrid layoutを使用します。grid layoutは2次元のレイアウトをするための機能で、$n$行$m$列に画面を分割できます。&lt;/p&gt;
&lt;p&gt;なので、この機能を使って画面を2行1列に分割してやります。&lt;/p&gt;
&lt;p&gt;まずは、下記のようなhtmlファイルを用意します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;ja&quot;&amp;gt; 
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf8&quot;&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;sample.css&quot;&amp;gt; 
    &amp;lt;title&amp;gt;sample html&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt; 
  &amp;lt;body&amp;gt;
    &amp;lt;div class=&quot;grid-area&quot;&amp;gt;
      &amp;lt;div class=&quot;top-area&quot;&amp;gt;
        Top
      &amp;lt;/div&amp;gt;
      &amp;lt;div class=&quot;bottom-area&quot;&amp;gt;
        Bottom
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そして、このようなCSSを用意してやると、上側と下側が8:2の割合で分割されると思います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.grid-area {
  display: grid;
  height: 100vh;                /* grid全体の高さを設定 */
  grid-template-rows: 80% 20%;  /* gridの割合が8:2になるよう2行用意 */
  grid-template-columns: 100%;  /* 横幅いっぱいに1列用意 */
}

.top-area {
  grid-row: 1;
  grid-column: 1;
}

.bottom-area {
  grid-row: 2;
  grid-column: 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こんな感じで8:2で領域が分割されます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./grid_layout2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;上側部分だけスクロールさせたい場合&lt;/h3&gt;
&lt;p&gt;さて、上記が基本となるのですが、このままですと上側部分に表示すべきコンテンツが増えてきた場合、このように下側部分まではみ出してきてしまいます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./grid_layout3-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;これを解決するために、コンテンツがはみ出した場合、上側部分のみスクロールするようにしてみます。先ほどのCSSに&lt;code&gt;overflow-y: scroll&lt;/code&gt;を追加してやるだけです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.grid-area {
  display: grid;
  height: 100vh;
  grid-template-rows: 80% 20%;
  grid-template-columns: 100%;
}

.top-area {
  grid-row: 1;
  grid-column: 1;
  overflow-y: scroll; /* これを追加。縦方向にコンテンツがあふれた場合スクロール可能として処理する */
}

.bottom-area {
  grid-row: 2;
  grid-column: 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;すると、下記のように下側にはみ出した場合は自動で上側部分のみスクロールするようになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./grid_layout4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考： &lt;a href=&quot;https://qiita.com/kura07/items/e633b35e33e43240d363&quot;&gt;CSS Grid Layout を極める！（基礎編） #CSS3 - Qiita&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>【OpenCV】decomposeProjectionMatrixを使って透視投影変換行列を分解する～完全版～</title><link>https://mem-archive.com/posts/2019/04/21/post-1778/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/04/21/post-1778/</guid><pubDate>Sun, 21 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./decomposeProjection3.jpg&quot;&lt;/p&gt;
&lt;p&gt;以前こんな記事を書きましたが、OpenCVの透視投影変換行列を分解する&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;には他にもいろいろと罠があったので、&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;を利用して透視投影変換行列を内部パラメータと外部パラメータに分解するための完全版の使い方をまとめていきます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/03/21/post-1704/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;まずは単純に分解&lt;/h2&gt;
&lt;p&gt;さて、まずは単純に&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;で透視投影変換行列を分解していきます。&lt;/p&gt;
&lt;p&gt;ただ、&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;は内部的にはQR分解を使用して行列分解しているだけなので、これだけではいろいろと問題が残ります。&lt;/p&gt;
&lt;h2&gt;内部パラメータをスケーリング&lt;/h2&gt;
&lt;p&gt;単純に分解しただけでは、正確な内部パラメータは得られるとは限りません。なぜなら、QR分解で得られるのは、”上三角行列”と”正規直行行列”で、上三角行列の右下要素が1になるという制約は入っていないからです。&lt;/p&gt;
&lt;p&gt;そのため、入力に使用した透視投影変換行列は定数倍されていたものの場合、上三角行列の右下要素が1ではない値になります。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/04/06/post-1751/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;なので、&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;で得られた内部パラメータの右下の要素で内部パラメータ全体を割ってやる必要があります。そうすることで右下要素が1の内部パラメータを得ることができます。&lt;/p&gt;
&lt;h2&gt;内部パラメータの焦点距離が負の数になった場合&lt;/h2&gt;
&lt;p&gt;前述のとおり、QR分解で得られるのは”上三角行列”と”正規直行行列”でした。すなわち、&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;で得られた内部パラメータの焦点距離の要素が、正の数になるとは限りません。&lt;br /&gt;
ですので、焦点距離が正の数になるよう補正する必要があります。&lt;/p&gt;
&lt;p&gt;先ほどまでに求めた内部パラメータを$\boldsymbol{K}$、回転行列を$\boldsymbol{R}$とおきます。さらに透視投影変換行列の1～3列目の部分を$\boldsymbol{P}_{3\times3}$とおきます。すると、これらの関係は下のように表すことができます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;P_33=KR&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;焦点距離が負の数になる場合は、下のように焦点距離が正になるようにできるように、内部パラメータと回転行列を補正します。このようにすると、回転行列を正規直行行列の状態に保ったまま、焦点距離の符号を変えることができます。&lt;/p&gt;
&lt;p&gt;この$\boldsymbol{K}&apos;$を最終的な内部パラメータ、$\boldsymbol{R}&apos;$を最終的な回転行列とします。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./decomposeProjection4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;なお、$\boldsymbol{K}$の1行1列目、2行2列目それぞれの焦点距離の符号が別の場合、そもそも透視投影変換行列の導出が何かおかしい可能性が高いので、そちらを見直した方がいいです。&lt;/p&gt;
&lt;h2&gt;並進ベクトルの導出&lt;/h2&gt;
&lt;p&gt;最後に、$\boldsymbol{R}&apos;$を使って下記で説明している方法で外部パラメータの並進ベクトルを導出すれば、内部パラメータ、外部パラメータの導出完了です。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/03/21/post-1704/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;OpenCVは大変便利に使わせてもらっていますが、正直公式のドキュメントの充実度がいまいちな感じなので、この辺の情報も充実してくれたら嬉しいですね。&lt;/p&gt;
&lt;p&gt;参考： &lt;a href=&quot;https://github.com/cdcseacave/openMVS/blob/063e5cd7f1139aceaf882f9f6ae3f0de26c59138/libs/MVS/Camera.cpp#L134-L155&quot;&gt;openMVS/libs/MVS/Camera.cpp at 063e5cd7f1139aceaf882f9f6ae3f0de26c59138 · cdcseacave/openMVS · GitHub&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>透視投影変換行列は定数倍しても問題なかった話</title><link>https://mem-archive.com/posts/2019/04/06/post-1751/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/04/06/post-1751/</guid><pubDate>Sat, 06 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./perspective8.jpg&quot;
import img2 from &quot;./perspective9.jpg&quot;
import img3 from &quot;./perspective10.jpg&quot;&lt;/p&gt;
&lt;p&gt;考えてみれば当たり前だったのですが、透視投影変換行列はスケール不定性的なものを持っていることに気づいたので、まとめてみます。&lt;/p&gt;
&lt;p&gt;そもそも透視投影変換について復習したい場合はこちらをご覧ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/26/post-30/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;透視投影変換の定数倍&lt;/h2&gt;
&lt;p&gt;透視投影変換行列とは、世界座標点を画像座標点に投影する行列のことでした。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;透視投影変換行列&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;この行列を$n$倍するとこうなります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;透視投影変換行列&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;さて、ここで$s&apos;=s/n$とおくと、この式は&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;透視投影変換行列&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;こうなって、結局最初の透視投影変換の式と同じ形になります。&lt;/p&gt;
&lt;p&gt;下記記事のような手法で2次元点、3次元点の組み合わせから透視投影変換行列を求めた場合、求まった透視投影変換行列は定数倍されたものになります。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/03/16/post-1646/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>【OpenCV】decomposeProjectionMatrixを使った外部パラメータの導出</title><link>https://mem-archive.com/posts/2019/03/21/post-1704/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/03/21/post-1704/</guid><pubDate>Thu, 21 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./perspective6.jpg&quot;
import img2 from &quot;./perspective7.jpg&quot;&lt;/p&gt;
&lt;p&gt;透視投影変換行列を内部パラメータと外部パラメータに分解するため、OpenCVの&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;を使用したのですが、ちょっと使い方にはまったので使い方メモです。&lt;/p&gt;
&lt;h2&gt;公式ドキュメントを見てみる&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;の使い方はここに書いてます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.opencv.org/3.0-last-rst/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#decomposeprojectionmatrix&quot;&gt;Camera Calibration and 3D Reconstruction — OpenCV 3.0.0-dev documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この関数の引数や返り値は以下の通りです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void decomposeProjectionMatrix(InputArray projMatrix, OutputArray cameraMatrix, OutputArray rotMatrix, OutputArray transVect, OutputArray rotMatrixX=noArray(), OutputArray rotMatrixY=noArray(), OutputArray rotMatrixZ=noArray(), OutputArray eulerAngles=noArray() )

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;各パラメータの役割はこんな感じです。（&lt;code&gt;rotMatrixX&lt;/code&gt;とかは今回触れないので省略してます。）&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;引数&lt;/th&gt;
&lt;th&gt;役割&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;入力&lt;/td&gt;
&lt;td&gt;projMatrix&lt;/td&gt;
&lt;td&gt;透視投影変換行列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出力&lt;/td&gt;
&lt;td&gt;cameraMatrix&lt;/td&gt;
&lt;td&gt;内部パラメータ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;rotMatrix&lt;/td&gt;
&lt;td&gt;外部パラメータの回転行列&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;transVect&lt;/td&gt;
&lt;td&gt;カメラ位置の示したベクトル&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;はまったところ&lt;/h2&gt;
&lt;p&gt;この関数の出力である&lt;code&gt;transVect&lt;/code&gt;、すなわちカメラ位置を示したベクトルがどの座標系で表現されているのかを確認せずに使ったので、見事にバグを生み出してしまいました。&lt;/p&gt;
&lt;h3&gt;カメラ位置は世界座標系で求まる&lt;/h3&gt;
&lt;p&gt;はい、結論から言うと、&lt;code&gt;transVect&lt;/code&gt;は世界座標系でのカメラ位置です。なので、外部パラメータで使用するような並進ベクトル$\boldsymbol{t}$を求めるには&lt;code&gt;transVect&lt;/code&gt;から導出してやる必要があります。&lt;/p&gt;
&lt;p&gt;なお、この変換には外部パラメータへの理解が必要となりますので、わからない人はこの記事をご覧ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/17/post-74/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;変換するにはまず、&lt;code&gt;transVect&lt;/code&gt;は4次元ベクトルとして返ってきて扱いづらいため、3次元ベクトルにしてやります。これは、1～3番目の要素を4番目の要素で割ればいいです。&lt;br /&gt;
この3次元ベクトルを$\boldsymbol{X}_w=[X_w, Y_w, Z_w]^T$と置きます。&lt;/p&gt;
&lt;p&gt;ついでに求まった回転行列&lt;code&gt;rotMatrix&lt;/code&gt;を$\boldsymbol{R}$と置いておきます。&lt;/p&gt;
&lt;p&gt;さらに、この世界座標系で表現されたカメラ位置$\boldsymbol{X_w}$は外部パラメータによって変換されるとカメラ座標系の原点の座標$\boldsymbol{X}_c=[0, 0, 0]$になるはずです。&lt;br /&gt;
すなわちこうなります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;数式&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;つまり、並進ベクトル$\boldsymbol{t}$はこのように求めることができます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;数式&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;なお、&lt;code&gt;decomposeProjectionMatrix&lt;/code&gt;には他にもハマりどころが何箇所かあるので、それはこちらにまとめています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/04/21/post-1778/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>2次元点-3次元点の対応付けからカメラパラメータを導出する</title><link>https://mem-archive.com/posts/2019/03/16/post-1646/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/03/16/post-1646/</guid><pubDate>Sat, 16 Mar 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import MoshimoAffiliate from &quot;@components/misc/MoshimoAffiliate.astro&quot;&lt;/p&gt;
&lt;p&gt;SfMやSLAMでは、2次元点と3次元点の対応付けから、カメラパラメータ(透視投影変換行列)を求める必要があります。今回は、DLT (Direct Linear Transformation)を使った透視投影変換行列の導出法について書いてみます。&lt;/p&gt;
&lt;p&gt;DLTの考え方はこの本に書いてあります（英語です）。&lt;/p&gt;
&lt;p&gt;&amp;lt;MoshimoAffiliate data={{
&quot;n&quot;:&quot;Multiple View Geometry in Computer Vision&quot;,
&quot;b&quot;:&quot;Cambridge University Press&quot;,
&quot;t&quot;:&quot;10232097&quot;,
&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,
&quot;c_p&quot;:&quot;&quot;,
&quot;p&quot;:[&quot;/images/I/410TxmdT9qL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],
&quot;u&quot;:{
&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/0521540518&quot;,
&quot;t&quot;:&quot;amazon&quot;,
&quot;r_v&quot;:&quot;&quot;
},
&quot;v&quot;:&quot;2.1&quot;,
&quot;b_l&quot;:[
{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/0521540518&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},
{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/Multiple%20View%20Geometry%20in%20Computer%20Vision/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},
{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=Multiple%20View%20Geometry%20in%20Computer%20Vision&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}
],
&quot;eid&quot;:&quot;m9QYA&quot;,
&quot;s&quot;:&quot;s&quot;
}} /&amp;gt;&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;ある画像の特徴点の2次元座標と、その特徴点の世界座標（3次元座標）がわかっているものとします。このとき、この世界座標点は透視投影変換を経て画像上に投影されているはずです。&lt;/p&gt;
&lt;p&gt;この透視投影変換行列を導出したいのです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./perspective5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;透視投影変換とはそもそも何者かわからない人は、こちらを参照ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/26/post-30/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;DLTを使った透視投影変換行列の導出&lt;/h2&gt;
&lt;p&gt;さて、上の透視投影変換の式を展開するとこのようになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./perspective2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;さらに、この式を$(u,v)$についてまとめると、このように$s$を削除した形式にできます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./perspective4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ここで、$\boldsymbol{A,x}$を次のように置いてみます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./dlt-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;すると、(1)の式は&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./dlt2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;という風に書き換えることができます。実際に式を展開してまとめると(1)と同じになるので、不安な人は試してみてください。&lt;/p&gt;
&lt;p&gt;2次元点、3次元点の対応付けが複数わかっていれば、行列$\boldsymbol{A}$はこう書くことができるので、&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./dlt3-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;先ほどの式はこうなります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./dlt4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;あとは、特異値分解とかを使ってこれを解いて$\boldsymbol{x}$を求めてやれば、透視投影変換行列を求めることができます。&lt;/p&gt;
&lt;p&gt;なお、透視投影変換行列の自由度は12で、2次元点と3次元点の対応付け1つで2つの方程式を作ることができるので、透視投影変換行列をこの方法で導出するには、最低6つの2次元点、3次元点の対応付けが既知である必要があります。&lt;/p&gt;
</content:encoded></item><item><title>【Chrome拡張開発】ポップアップに表示したリンクが開けない場合の対処</title><link>https://mem-archive.com/posts/2019/02/23/post-1618/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/02/23/post-1618/</guid><pubDate>Sat, 23 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;前回、Chrome拡張機能のポップアップの表示について書きました。このポップアップにhrefでリンクを追加したのですが、リンクをうまく開けなかったので、その対応メモです。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/02/17/post-1569/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;ポップアップにリンクを追加して、リンククリック時に新しいタブで開きたいです。&lt;/p&gt;
&lt;p&gt;こんな感じでポップアップのhtmlにリンクを追加するだけでいけるかなと思ったのですが、リンクは出現するもののクリックしても何も起こりません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;popup.css&quot;&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;h3&amp;gt;Chrome Extension Template&amp;lt;/h3&amp;gt;
    &amp;lt;a href=&quot;https://google.com&quot;&amp;gt;https://google.com&amp;lt;/a&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./extension4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;実現方法&lt;/h2&gt;
&lt;p&gt;ポップアップのリンクを新しいタブで開くには、&lt;code&gt;chrome.tabs.create&lt;/code&gt;を使用します。下記のようなjavascriptを用意します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// popup.js
$(function () {
  $(&apos;#link&apos;).on(&apos;click&apos;, (e) =&amp;gt; {
    chrome.tabs.create({url: $(e.target).attr(&apos;href&apos;)});
  });
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このjsファイルをポップアップのhtmlで読み込めば、ポップアップのリンクをクリック時に、新しいタブで開くようになります。JQueryを使っているのでJQueryも読み込んでいます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;popup.css&quot;&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot; src=&quot;jquery-3.3.1.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;text/javascript&quot; src=&quot;popup.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;

  &amp;lt;body&amp;gt;
    &amp;lt;h3&amp;gt;Chrome Extension Template&amp;lt;/h3&amp;gt;
    &amp;lt;a id=&quot;link&quot; href=&quot;https://google.com&quot;&amp;gt;https://google.com&amp;lt;/a&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;p&gt;https://teratail.com/questions/93432&lt;/p&gt;
</content:encoded></item><item><title>【Chrome拡張開発】拡張機能アイコンのクリック時にポップアップを表示する</title><link>https://mem-archive.com/posts/2019/02/17/post-1569/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/02/17/post-1569/</guid><pubDate>Sun, 17 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近Chrome拡張を開発してみてるので、その開発メモです。基本的には以下のGoogleの公式リファレンスを参考にしてます。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://developer.chrome.com/docs/extensions/mv2/reference/browserAction&quot;&gt;chrome.browserAction  |  Reference  |  Chrome for Developers&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;やりたいこと&lt;/h2&gt;
&lt;p&gt;こんな感じで、Chromeのツールバーにある拡張機能のアイコンをクリックしたら、設定画面的なものを表示させたいです。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./extension.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;実現方法&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;manifest.json&lt;/code&gt;に以下を追記します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;browser_action&quot;: {
  &quot;default_icon&quot;: &quot;icon.png&quot;,                // ツールバーに表示される拡張機能のアイコン。無くても可（デフォルトアイコンになる）。
  &quot;default_title&quot;: &quot;Hide Unnecessary Site&quot;,  // ツールバーの拡張機能アイコンにマウスオーバーしたときに表示されるテキスト。無くても可。
  &quot;default_popup&quot;: &quot;popup.html&quot;              // 拡張機能アイコンをクリックしたときに表示されるHTML
},

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最低限必要なのは&lt;code&gt;&quot;default_popup&quot;&lt;/code&gt;になります。ここに設定した&lt;code&gt;popup.html&lt;/code&gt;をこんな感じで作ってみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h3&amp;gt;Chrome Extension Template&amp;lt;/h3&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chromeにこの拡張機能を読み込ませて、ツールバーの拡張機能アイコンをクリックしてみます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./extension2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;無事&lt;code&gt;popup.html&lt;/code&gt;の内容が表示されました。でも、なんだかポップアップが小さいような。。。&lt;/p&gt;
&lt;p&gt;調べてみると、cssでwidthを設定すればうまい具合の大きさにできそうです。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://stackoverflow.com/questions/5020953/popup-html-change-width&quot;&gt;google chrome extension - popup.html - change width? - Stack Overflow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;上記を参考にcssで&lt;code&gt;min-width&lt;/code&gt;を指定してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* popup.css */
body {
  min-width: 300px;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そしてこのcssを&lt;code&gt;popup.html&lt;/code&gt;で読み込みます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;popup.css&quot;&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h3&amp;gt;Chrome Extension Template&amp;lt;/h3&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事いい感じの大きさでポップアップを表示できました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./extension.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Pythonのopenで改行コードが変わった話 in Windows</title><link>https://mem-archive.com/posts/2019/02/17/post-1579/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/02/17/post-1579/</guid><pubDate>Sun, 17 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pythonでちょっとしたスクリプトを書いていたのですが、どうにも入力ファイルと出力ファイルの改行コードが変わってしまってちょっとはまったので、対応メモです。&lt;/p&gt;
&lt;p&gt;環境はWindows 10 + Python3.5になります。&lt;/p&gt;
&lt;p&gt;そもそも改行コードの確認方法がわからない人は、こちらを参考にしてください。&lt;/p&gt;
&lt;p&gt;https://mem-archive.com/2018/12/23/post-1308/&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;このようにファイルを1行ずつ読み込んで出力するだけのプログラムを作ります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;with open(&quot;./input.txt&quot;) as f:
  for line in f:
    print(line, end=&apos;&apos;)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;input.txt&lt;/code&gt;の改行コードは&lt;code&gt;\r\n&lt;/code&gt;です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ od -c input.txt
0000000   h   o   g   e  \r  \n   f   u   g   a  \r  \n   p   i   y   o
0000020

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行して結果をファイルに出力します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python3 main.py &amp;gt; output.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;output.txt&lt;/code&gt;の改行コードは&lt;code&gt;\n&lt;/code&gt;になってしまいます。ついでにファイルの最後にも改行コードが付与されています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ od -c output.txt
0000000   h   o   g   e  \n   f   u   g   a  \n   p   i   y   o  \n
0000017

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解決策&lt;/h2&gt;
&lt;p&gt;先ほどのプログラムをこのように修正します。&lt;code&gt;open&lt;/code&gt;に&lt;code&gt;newline&lt;/code&gt;引数を追加してやりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;with open(&quot;./input.txt&quot;, newline=&apos;&apos;) as f:
  for line in f:
    print(line, end=&apos;&apos;)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このプログラムで出力されたファイルの改行コードは、変換されずにそのままになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ od -c output.txt
0000000   h   o   g   e  \r  \n   f   u   g   a  \r  \n   p   i   y   o
0000020

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;newline&lt;/code&gt;を指定しない場合、デフォルトでは&lt;code&gt;None&lt;/code&gt;になります。このときの動作は、&lt;code&gt;\r&lt;/code&gt;、&lt;code&gt;\n&lt;/code&gt;、&lt;code&gt;\r\n&lt;/code&gt;を改行コードとしてみなして、勝手に全部&lt;code&gt;\n&lt;/code&gt;として扱います。&lt;br /&gt;
&lt;code&gt;newline=&apos;&apos;&lt;/code&gt;とすると、改行コードを変換せずそのまま扱います。&lt;/p&gt;
&lt;p&gt;試してはいませんが、&lt;code&gt;newline&lt;/code&gt;に&lt;code&gt;\r\n&lt;/code&gt;とかを指定すれば、&lt;code&gt;\r\n&lt;/code&gt;だけを改行コードとして扱えたりもするらしいです。&lt;/p&gt;
&lt;p&gt;勝手に改行コード変えられると思わぬところで躓くことがあると思うのですが、なぜデフォルトで変換するような挙動になってるんでしょうね。&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;p&gt;https://docs.python.jp/3/library/functions.html&lt;/p&gt;
</content:encoded></item><item><title>【JQuery】Uncaught TypeError: xxx.find is not a function at yyyy.jsの原因と解決法</title><link>https://mem-archive.com/posts/2019/02/03/post-1535/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/02/03/post-1535/</guid><pubDate>Sun, 03 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;フロントエンド開発に慣れてなくて、JQueryの初歩的な部分ではまったのでメモです。&lt;/p&gt;
&lt;h2&gt;エラー&lt;/h2&gt;
&lt;p&gt;エラーを吐いたコードはこれです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let elements = $(&apos;.g&apos;);

for(let i = 0; i &amp;lt; elements.length; i++) {
  console.log(elements[i].find(&apos;.LC20lb&apos;));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行すると、下記のようなエラーが出力されました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Uncaught TypeError: elements[i].find is not a function
    at main.js:4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;原因と解決法&lt;/h2&gt;
&lt;p&gt;変数&lt;code&gt;elements&lt;/code&gt;はJQueryのオブジェクトではなく、JavaScriptのオブジェクトであることが、このエラーの原因です。&lt;/p&gt;
&lt;p&gt;なので、問題のこの部分を&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  console.log(elements[i].find(&apos;.LC20lb&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こうやってJQueryのオブジェクトに変換してやると、動くようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  console.log($(elements[i]).find(&apos;.LC20lb&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://stackoverflow.com/questions/24300762/jquery-throws-an-error-that-element-find-is-not-a-function&quot;&gt;javascript - jQuery throws an error that element.find() is not a function - Stack Overflow&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Hyperのデフォルトショートカットキー(Windows)</title><link>https://mem-archive.com/posts/2019/01/31/post-1498/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/01/31/post-1498/</guid><pubDate>Thu, 31 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;WindowsのターミナルソフトはHyperを使ってます。なかなかいい感じの使い勝手なのですが、まだ慣れてなくてショートカットキーを頻繁に忘れるので、ショートカットキーをメモしておきます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/01/06/post-1336/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;ショートカットキー一覧&lt;/h2&gt;
&lt;p&gt;デフォルトショートカットキーを定義したファイルはこれです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/zeit/hyper/blob/canary/app/keymaps/win32.json&quot;&gt;hyper/app/keymaps/win32.json at canary · vercel/hyper · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;しかし、自分の環境だと一部ショートカットキーが微妙に異なったり、動作しないのがあったので、動作確認できたショートカットをまとめてみました。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;ショートカット&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;説明&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + i&lt;/td&gt;
&lt;td&gt;デベロッパーツールの起動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + r&lt;/td&gt;
&lt;td&gt;ウィンドウのクリア   同ウィンドウ内のタブとかの表示すべてクリアされる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + F5&lt;/td&gt;
&lt;td&gt;ウィンドウのクリア   同ウィンドウ内のタブとかをすべて閉じ、起動したての状態と同じになる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + ,&lt;/td&gt;
&lt;td&gt;設定ファイル(.hyper.js)を開く&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + 0&lt;/td&gt;
&lt;td&gt;フォントサイズをデフォルトにく&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + ;&lt;/td&gt;
&lt;td&gt;フォントサイズを大きく&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + -&lt;/td&gt;
&lt;td&gt;フォントサイズを小さく&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + n&lt;/td&gt;
&lt;td&gt;新規ウィンドウの起動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + m&lt;/td&gt;
&lt;td&gt;ウィンドウの最小化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F11&lt;/td&gt;
&lt;td&gt;ウィンドウのフルスクリーン化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + q   Alt + F4&lt;/td&gt;
&lt;td&gt;ウィンドウを閉じる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + ]   Ctrl + Shift + →   Ctrl + Alt + →   Ctrl + Tab&lt;/td&gt;
&lt;td&gt;右のタブへ移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + [   Ctrl + Shift + ←   Ctrl + Alt + ←   Ctrl + Shift + Tab&lt;/td&gt;
&lt;td&gt;左のタブへ移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + 数字&lt;/td&gt;
&lt;td&gt;数字番目のタブへ移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + PageUp&lt;/td&gt;
&lt;td&gt;次のPaneへ移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + PageDown&lt;/td&gt;
&lt;td&gt;前のPaneへ移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + d&lt;/td&gt;
&lt;td&gt;Paneを縦に分割&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + e&lt;/td&gt;
&lt;td&gt;Paneを横に分割&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + w&lt;/td&gt;
&lt;td&gt;Paneを閉じる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + c&lt;/td&gt;
&lt;td&gt;コピー&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + v&lt;/td&gt;
&lt;td&gt;ペースト&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + a&lt;/td&gt;
&lt;td&gt;全選択&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + ←&lt;/td&gt;
&lt;td&gt;前の単語へカーソルを移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + →&lt;/td&gt;
&lt;td&gt;次の単語へカーソルを移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Home&lt;/td&gt;
&lt;td&gt;先頭へカーソルを移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End&lt;/td&gt;
&lt;td&gt;末尾へカーソルを移動&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + BackSpace&lt;/td&gt;
&lt;td&gt;前の単語を削除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Del&lt;/td&gt;
&lt;td&gt;次の単語を削除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + k&lt;/td&gt;
&lt;td&gt;バッファの削除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ctrl + Shift + u&lt;/td&gt;
&lt;td&gt;プラグインの更新&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded></item><item><title>Vegetaを使ったサーバの負荷テスト</title><link>https://mem-archive.com/posts/2019/01/21/post-1443/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/01/21/post-1443/</guid><pubDate>Mon, 21 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;仕事でサーバの負荷テストをすることになったので、Vegetaという負荷ツールを使ってみようと思いました。githubの画像がとても素敵ですね。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://github.com/tsenart/vegeta&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Vegetaのインストール&lt;/h2&gt;
&lt;p&gt;まずはVegetaをインストールします。インストールにはGo言語の開発環境が必要なので、環境を作ってない人はこちらを参考に構築してください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/02/post-1220/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;VegetaはGo言語製のツールなので、&lt;code&gt;go get&lt;/code&gt;コマンドでインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go get -u github.com/tsenart/vegeta

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Vegetaの使い方&lt;/h2&gt;
&lt;p&gt;負荷テストは&lt;code&gt;vegeta attack&lt;/code&gt;というコマンドを使用します。このコマンド名個人的に大好きです。&lt;/p&gt;
&lt;h3&gt;GETリクエストで1つのURLへの負荷テスト&lt;/h3&gt;
&lt;p&gt;1つの特定のURLへのGETリクエストでのサーバの負荷テストはこうやります。&lt;code&gt;duration=5s&lt;/code&gt;、&lt;code&gt;rate=10&lt;/code&gt;は、5秒間の間、10 req/secで指定したURLにアクセスすることを示しています。&lt;/p&gt;
&lt;p&gt;結果は&lt;code&gt;result.bin&lt;/code&gt;というファイルに書き出しています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo &quot;GET http://192.168.3.2:8080&quot; | vegeta attack -duration=5s -rate=10 &amp;gt; result.bin

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;サーバのアクセス履歴を見ると、ちゃんとアクセスが来ていることがわかります。なお、サーバにはNginxを使っています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo tail /var/log/nginx/access.log
10.0.2.2 - - [20/Jan/2019:21:13:39 +0900] &quot;GET / HTTP/1.1&quot; 200 612 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;
10.0.2.2 - - [20/Jan/2019:21:13:39 +0900] &quot;GET / HTTP/1.1&quot; 200 612 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;
...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;結果は下記のように見ます。なお、&lt;code&gt;-type=json&lt;/code&gt;オプションを付与することで、JSON形式でも出力可能です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat result.bin | vegeta report
Requests      [total, rate]            50, 10.20
Duration      [total, attack, wait]    4.9028774s, 4.9006794s, 2.198ms
Latencies     [mean, 50, 95, 99, max]  2.07302ms, 2.0215ms, 2.919ms, 3.765ms, 3.765ms
Bytes In      [total, mean]            30600, 612.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  100.00%
Status Codes  [code:count]             200:50
Error Set:

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;複数のURLへの負荷テスト&lt;/h3&gt;
&lt;p&gt;複数のURLへのアクセスで負荷テストをする場合は、まずアクセスしたいURLを書いたファイルを用意します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat request_url.txt
GET http://192.168.3.2:8080
GET http://192.168.3.2:8080/hoge
GET http://192.168.3.2:8080/fuga

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-targets&lt;/code&gt;オプションにこのファイルを指定してやることで、複数のURLへアクセスする負荷テストを行うことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vegeta attack -duration=5s -rate=10 -targets=request_url.txt &amp;gt; result.bin

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;アクセスログを確認すると、ちゃんと指定した3つのURLへ均等にアクセスできていることを確認できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo tail /var/log/nginx/access.log
...
10.0.2.2 - - [21/Jan/2019:01:54:25 +0900] &quot;GET / HTTP/1.1&quot; 200 612 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;10.0.2.2 - - [21/Jan/2019:01:54:25 +0900] &quot;GET /hoge HTTP/1.1&quot; 200 8 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;10.0.2.2 - - [21/Jan/2019:01:54:25 +0900] &quot;GET /fuga HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;
...

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;POSTリクエストでの負荷テスト&lt;/h3&gt;
&lt;p&gt;POSTリクエストを用いる場合も&lt;code&gt;-targets&lt;/code&gt;オプションを使用します。まずは送りたいbodyを書いたファイルを用意します。JSONファイルを使った例を用いたサイトが多いようですが、別にJSONでなくても問題ありません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat parameter.txt
hoge=1&amp;amp;fuga=2&amp;amp;piyo=3

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらに、URL、必要ならheader、bodyを書いたファイルを用意します。なお、bodyを書いたファイルパスを指定する場合は先頭に&lt;code&gt;@&lt;/code&gt;を付与します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat request_url.txt
POST http://192.168.3.2:8080/piyo
Content-Type: application/x-www-form-urlencoded
@parameter.txt

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、&lt;code&gt;-targets&lt;/code&gt;オプションにURLやbodyを記したファイルを指定することで、POSTリクエストの負荷テストを実現できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vegeta attack -duration=5s -rate=10 -targets=request_url.txt &amp;gt; result.bin

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ログでもちゃんとPOST判定されてます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo tail /var/log/nginx/access.log
...
10.0.2.2 - - [21/Jan/2019:01:47:47 +0900] &quot;POST /piyo HTTP/1.1&quot; 200 86 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;10.0.2.2 - - [21/Jan/2019:01:47:47 +0900] &quot;POST /piyo HTTP/1.1&quot; 200 86 &quot;-&quot; &quot;Go-http-client/1.1&quot; &quot;-&quot;
...

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Create React Appで作成したアプリを任意のサブディレクトリに配置する</title><link>https://mem-archive.com/posts/2019/01/14/post-1396/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/01/14/post-1396/</guid><pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;Create React Appで作成したサービスをサーバのサブディレクトリ直下に配置したら上手く動かなかったので、その対応メモです。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;Create React Appを使ってみました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/01/08/post-1370/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;それで、ローカルでは動作確認できたものの、作成したサービス用のサブディレクトリを作成してサービスを配置しようとしたら、真っ白い画面が表示されました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./react2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;デベロッパーツールを開いて確認したところ、JavaScriptとCSSの読み込みに軒並み失敗している模様。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./react3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;原因&lt;/h2&gt;
&lt;p&gt;スクショからわかるように&lt;code&gt;http://192.168.3.2:8080/reactapp/&lt;/code&gt; というURLに作成したサービスを割り当てました。&lt;/p&gt;
&lt;p&gt;サーバにはNginxを使用しているので、サーバの&lt;code&gt;/usr/share/nginx/html/reactapp/&lt;/code&gt;にCreate React Appのbuild以下のファイルとディレクトリをそのまま配置しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/11/18/post-1171/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;なので、JavaScriptとかCSSのURLは正しくは&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://192.168.3.2:8080/reactapp/static/js/ファイル名
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;となるべきなのですが、&lt;code&gt;index.html&lt;/code&gt;ではURLからreactappが抜けた&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://192.168.3.2:8080/static/js/ファイル名
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;でアクセスしようとしていたので、404エラーになっていました。&lt;/p&gt;
&lt;h2&gt;対応&lt;/h2&gt;
&lt;p&gt;まずはcross-envをインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm install --save-dev cross-env

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そして&lt;code&gt;package.json&lt;/code&gt;のこの部分を&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot;: {
  ...
  &quot;build&quot;: &quot;react-scripts build&quot;,
  ...
},

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こう変えます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot;: {
  ...
  &quot;build&quot;: &quot;cross-env PUBLIC_URL=/reactapp/ react-scripts build&quot;,
  ...
},

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで以下でビルドして&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm run build

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;http://192.168.3.2:8080/reactapp/&lt;/code&gt;に配置すると、正常に動くようになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./react4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Create React Appを使ったサービス開発</title><link>https://mem-archive.com/posts/2019/01/08/post-1370/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/01/08/post-1370/</guid><pubDate>Tue, 08 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;最近いい加減フロントエンド開発の知識もないとまずいなと思い始めたので、Reactを触ってみることにしました。どうにもCreate React Appというツールを使うと簡単にReactを使ったサービスを作り始めることができるみたいなので、今回はこのツールを使ってみます。&lt;/p&gt;
&lt;p&gt;基本的に公式のGetting Startedに従って導入を進めます。なお環境はWindows 10のWSL (Windows Subsystem for Linux)を使用します。つまりはLinuxです。&lt;/p&gt;
&lt;p&gt;後日Macでも試しましたが、同じ手順で導入できました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://github.com/react/create-react-app&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;:::warning
Create React Appを&lt;code&gt;npm install -g create-react-app&lt;/code&gt;で自分のローカル環境にインストールすることは非推奨となりました。&lt;br /&gt;
npxを使えばローカル環境にインストールせずともCreate React Appを使えるので、以前ローカル環境に入れちゃった人は&lt;code&gt;npm uninstall -g create-react-app&lt;/code&gt;で消しておきましょう。
:::&lt;/p&gt;
&lt;h2&gt;Reactアプリの作成&lt;/h2&gt;
&lt;p&gt;Reactアプリの雛型を作ります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npx create-react-app my-app

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create React Appを使うとこのコマンドだけで雛型を作れるのは素晴らしいですね。&lt;/p&gt;
&lt;h2&gt;作成したアプリの実行&lt;/h2&gt;
&lt;p&gt;ひとまず雛型のままの状態で実行してみます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd my-app
$ npm start

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでアプリが実行されましたので、http://localhost:3000/ にアクセスするとReactの画面が表示されると思います。これが表示されたら成功です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./react.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;本番環境用のファイルを作成&lt;/h2&gt;
&lt;p&gt;実際にWebサービスとして公開する場合は、公開用のファイルを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm run build

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでbuildディレクトリの中に公開用ファイルができるので、buildディレクトリの中身をサーバに転送して配置すれば公開完了です。&lt;/p&gt;
&lt;p&gt;なお、任意のサブディレクトリでサービスを公開する場合は、&lt;code&gt;package.json&lt;/code&gt;を少しだけ書き換えてやる必要がありますので、こちらを参考にしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/01/14/post-1396/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;また、作成された&lt;code&gt;build/index.html&lt;/code&gt;を直接開くとCSSとかJavaScriptを上手く読めこめなくて真っ白い画面が表示されるので、慌てないようにしましょう。&lt;/p&gt;
</content:encoded></item><item><title>Hyper + WSLでWindows上に快適なターミナル環境の構築</title><link>https://mem-archive.com/posts/2019/01/06/post-1336/</link><guid isPermaLink="true">https://mem-archive.com/posts/2019/01/06/post-1336/</guid><pubDate>Sun, 06 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;以前Windows上で快適なターミナル環境を整えるため、cmderを導入しましたが、不満が出てきたので、Hyperというターミナルを試してみましたので、その環境構築メモです。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/03/10/post-376/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Hyperのインストール&lt;/h2&gt;
&lt;p&gt;まずはこちらからHyperをダウンロード&amp;amp;インストールします。基本的にダウンロードしたexeファイルを実行するだけでインストールされます。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://hyper.is/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Hyperのデフォルトシェルをbashにする&lt;/h2&gt;
&lt;p&gt;Hyperインストール後のデフォルトはまだbashになってないので、設定を変更して起動時にbashが起動するようにします。&lt;/p&gt;
&lt;p&gt;メニューから&lt;code&gt;Edit → Preferences&lt;/code&gt;を開きます。すると設定ファイルがメモ帳で開かれるので、これを編集してbashが起動するようにします。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./hyper.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;実際のファイルは(特に変更してなければ)、&lt;code&gt;C:\Users\ユーザ名\.hyper.js&lt;/code&gt;なので、メモ帳以外のテキストエディタで設定ファイルを開きたい場合は、このファイルを直接開きましょう。&lt;/p&gt;
&lt;p&gt;編集するのはこう書いてある行です。この行を&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shell: &apos;&apos;,

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こう編集します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shell: &apos;C:\\Windows\\System32\\wsl.exe&apos;,

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらにこの行を&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shellArgs: [&apos;--login&apos;],

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こう編集します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;shellArgs: [&apos;~&apos;],

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--login&lt;/code&gt;を残したままだと起動に失敗するようになるので気をつけましょう。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;~&lt;/code&gt;で起動時のカレントディレクトリを指定しているので、好きなパスに変えても問題ありません。ここに何も書かなければ&lt;code&gt;/mnt/c/Users/ユーザ名&lt;/code&gt;がデフォルト値となるようです。&lt;/p&gt;
&lt;p&gt;これで、次回起動時からbashがデフォルトで起動するようになります。&lt;/p&gt;
&lt;p&gt;デフォルトシェルをzshなどに変えたい場合は、こちらを参考にデフォルトシェルを変更してください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/11/18/post-1163/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;また、Hyperのショートカットキーについてはこちらを参考にしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/01/31/post-1498/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://fcbrossard.net/blog/wsl-ubuntu-zsh-hyper-terminal&quot;&gt;WSL / Ubuntu / ZSH and Hyper Terminal | fcbrossard.net&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Linuxで改行コードを確認/変更する</title><link>https://mem-archive.com/posts/2018/12/23/post-1308/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/12/23/post-1308/</guid><pubDate>Sun, 23 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;同僚からもらったテキストファイルの改行コードがWindowsの&lt;code&gt;\r\n&lt;/code&gt;だったせいで、解析用のシェルスクリプトが上手く回らなかったので、改行コードの確認と変更方法のメモです。&lt;/p&gt;
&lt;h2&gt;OS毎の改行コード&lt;/h2&gt;
&lt;p&gt;WindowsとLinux、OS Xの改行コードは&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;改行コード&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\r\n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux (Unix)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS X&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;となっています。&lt;/p&gt;
&lt;h2&gt;改行コードの確認&lt;/h2&gt;
&lt;p&gt;まずは改行コードの確認をします。確認には&lt;code&gt;od&lt;/code&gt;コマンドを使用します。デフォルトでは&lt;code&gt;od&lt;/code&gt;は8進数でファイル内容をダンプしますが、cオプションを付けることでASCIIコードとしてダンプされるので、改行コードが目に見えるようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 実際のファイルの中身
$ cat hoge.txt
hoge
fuga
piyo

# ASCIIコードでダンプ
$ od -c hoge.txt
0000000   h   o   g   e  \r  \n   f   u   g   a  \r  \n   p   i   y   o
0000020  \r  \n  \r  \n
0000024

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;はい、改行コードが&lt;code&gt;\r\n&lt;/code&gt;なのが確認できました。これを&lt;code&gt;\n&lt;/code&gt;に変換します。&lt;/p&gt;
&lt;h2&gt;改行コードの変更&lt;/h2&gt;
&lt;p&gt;改行コードの変換には&lt;code&gt;dos2unix&lt;/code&gt;コマンドを使用します。コマンドが見つからない場合は&lt;code&gt;apt&lt;/code&gt;や&lt;code&gt;yum&lt;/code&gt;でインストールしてください。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Debian/Ubuntu系のLinuxの場合
$ sudo apt install dos2unix

# CentOS系のLinuxの場合
$ sudo yum install dos2unix

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;いよいよ&lt;code&gt;dos2unix&lt;/code&gt;コマンドで改行コードを変換します。使い方は簡単で、変換したいファイル名を指定するだけです。ファイルは上書きされるので注意してください。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Windowsの改行コードをLinuxの改行コードへ変換。元のファイルに上書きされる
$ dos2unix hoge.txt

# 改行コードを確認。\nになっていることが確認できる
$ od -c hoge.txt
0000000   h   o   g   e  \n   f   u   g   a  \n   p   i   y   o  \n  \n
0000020

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでWindowsの改行コードをLinuxの改行コードに変換することができました。&lt;/p&gt;
&lt;p&gt;ちなみに、反対にLinuxの改行コードからWindowsの改行コードへの変換は&lt;code&gt;unix2dos&lt;/code&gt;コマンドを使えばできます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Linuxの改行コードをWindowsの改行コードへ変換
$ unix2dos hoge.txt

# 改行コードを確認。\r\nになっていることを確認できる
$ od -c hoge.txt
0000000   h   o   g   e  \r  \n   f   u   g   a  \r  \n   p   i   y   o
0000020  \r  \n  \r  \n
0000024

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;Efficient Linuxコマンドライン ―開発と自分に磨きをかけるLinuxのテクノロジー&quot;,&quot;b&quot;:&quot;オーム社&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/41Mvx10W-wL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/4814400489&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/4814400489&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/Efficient%20Linux%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%20%E2%80%95%E9%96%8B%E7%99%BA%E3%81%A8%E8%87%AA%E5%88%86%E3%81%AB%E7%A3%A8%E3%81%8D%E3%82%92%E3%81%8B%E3%81%91%E3%82%8BLinux%E3%81%AE%E3%83%86%E3%82%AF%E3%83%8E%E3%83%AD%E3%82%B8%E3%83%BC/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=Efficient%20Linux%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%20%E2%80%95%E9%96%8B%E7%99%BA%E3%81%A8%E8%87%AA%E5%88%86%E3%81%AB%E7%A3%A8%E3%81%8D%E3%82%92%E3%81%8B%E3%81%91%E3%82%8BLinux%E3%81%AE%E3%83%86%E3%82%AF%E3%83%8E%E3%83%AD%E3%82%B8%E3%83%BC&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;EtS0x&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-EtS0x&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Nginx + GolangでWebアプリケーション開発 ～http編～</title><link>https://mem-archive.com/posts/2018/12/17/post-1290/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/12/17/post-1290/</guid><pubDate>Mon, 17 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;NginxとGo言語でWebアプリケーションの開発環境を整えたので、構築メモです。&lt;/p&gt;
&lt;p&gt;環境はVirtualBox上で動いているCentOS 7になります。Nginxでアクセスを受けて、httpでGoに渡してGoで処理をさせたいと思います。&lt;/p&gt;
&lt;p&gt;FastCGI編はこちら&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/16/post-1252/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Nginxの設定ファイルを修正&lt;/h2&gt;
&lt;p&gt;まだNginxをインストールしていない方はこちら&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/11/18/post-1171/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;何があっても大丈夫なように、まず設定ファイルのバックアップを取っておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup2

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/etc/nginx/conf.d/default.conf&lt;/code&gt;を次のように修正します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen       80;
    server_name  localhost;

    location /fuga {
        proxy_pass      http://127.0.0.1:9000;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;confファイルに間違いがないか確認します。下記のように表示されたら問題ありません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定ファイルを再読み込みさせるために、Nginxを再起動させます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl restart nginx

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Goのプログラムを作成&lt;/h2&gt;
&lt;p&gt;Goの開発環境を作っていない方はこちら&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/02/post-1220/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;先ほど設定した9000番ポートへアクセスしたら、&lt;code&gt;Hello World!!!&lt;/code&gt;と出力するプログラムを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
        &quot;fmt&quot;
        &quot;net/http&quot;
)

func main() {
        http.HandleFunc(&quot;/fuga&quot;, handler)
        http.ListenAndServe(&quot;:9000&quot;, nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, &quot;Hello World!!!&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとはこいつを実行して、&lt;code&gt;http://localhsot/fuga&lt;/code&gt;にアクセスすると、&lt;code&gt;Hello World!!!&lt;/code&gt;が返ってきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# サーバ上から動作確認
$ curl http://localhost/fuga
Hello World!!!

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ブラウザ経由でアクセスしても&lt;code&gt;Hello World!!!&lt;/code&gt;が表示されることが確認できるかと思います。&lt;br /&gt;
(VirutualBoxのポートフォワーディング機能を使用しているため、8080ポートのアクセスがCentOSの80ポートへ転送される設定になっています。)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./nginx_go3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;http://pokrkami.hatenablog.com/entry/2016/01/27/233048&quot;&gt;nginxでgoを動かす - 冷やしブログはじめました&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Nginx + GolangでWebアプリケーション開発 ～FastCGI編～</title><link>https://mem-archive.com/posts/2018/12/16/post-1252/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/12/16/post-1252/</guid><pubDate>Sun, 16 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;&lt;/p&gt;
&lt;p&gt;NginxとGo言語でWebアプリケーションの開発環境を整えたので、構築メモです。&lt;/p&gt;
&lt;p&gt;環境はVirtualBox上で動いているCentOS 7になります。Fast CGIを使ってアクセスをNginxで受け取ってGoで処理をさせたいと思います。&lt;/p&gt;
&lt;p&gt;http編はこちら&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/17/post-1290/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Nginxの設定ファイルを修正&lt;/h2&gt;
&lt;p&gt;まだNginxをインストールしていない場合はこちらを参考にNginxを導入してください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/11/18/post-1171/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;まずはNginxを壊しても元に戻せるよう、設定ファイルのバックアップを取っておきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでconfがおかしくなっても元に戻せるので、やりたい放題できるようになりました。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/nginx/conf.d/default.conf&lt;/code&gt;を次のように修正してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen       80;
    server_name  localhost;

    location /hoge {
        fastcgi_pass    127.0.0.1:9000;
        include         /etc/nginx/fastcgi_params;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この設定で、&lt;code&gt;http://localhost/hoge&lt;/code&gt;以下へリクエストを送ると、&lt;code&gt;127.0.0.1:9000&lt;/code&gt;へアクセスされるようになります。&lt;/p&gt;
&lt;p&gt;confファイルが正しいかを確認します。下記のように表示されたら問題ありません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみに、正しくないconfだとこんな感じのエラーが表示されます。confのセミコロンを抜いてみました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo nginx -t
nginx: [emerg] unexpected &quot;}&quot; in /etc/nginx/conf.d/default.conf:8
nginx: configuration file /etc/nginx/nginx.conf test failed

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定ファイルを再読み込みさせるために、Nginxを再起動します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl restart nginx

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Goのプログラムを作成&lt;/h2&gt;
&lt;p&gt;まだGoの開発環境を整えていない場合は、こちらを参考に環境構築をしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/12/02/post-1220/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;先ほど設定した9000番ポートへのアクセスしたら、&lt;code&gt;Hello World!!&lt;/code&gt;と出力するプログラムを作成します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
        &quot;fmt&quot;
        &quot;net&quot;
        &quot;net/http&quot;
        &quot;net/http/fcgi&quot;
)

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, &quot;Hello World!!&quot;)
}

func main() {
        l, _ := net.Listen(&quot;tcp&quot;, &quot;127.0.0.1:9000&quot;)
        http.HandleFunc(&quot;/&quot;, handler)
        fcgi.Serve(l, nil)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとはこいつを実行して、&lt;code&gt;http://localhost/hoge&lt;/code&gt;以下にアクセスすると、&lt;code&gt;Hello World!!&lt;/code&gt;が返ってきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# サーバ上で動作確認
$ curl http://localhost/hoge
Hello World!!

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ブラウザ経由でアクセスしても、&lt;code&gt;Hello World!!&lt;/code&gt;が表示されることが確認できるかと思います。&lt;br /&gt;
(VirutualBoxのポートフォワーディング機能を使用しているため、8080ポートのアクセスがCentOSの80ポートへ転送される設定になっています。)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./nginx_go.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;http://umegusa.hatenablog.jp/entry/2015/02/22/025832&quot;&gt;Nginx + Golang でWebアプリケーション開発を試してみた - umegusa&apos;s blog&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Linux(CentOS, Ubuntu)にGo言語の開発環境を構築する</title><link>https://mem-archive.com/posts/2018/12/02/post-1220/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/12/02/post-1220/</guid><pubDate>Sun, 02 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Go言語の開発環境を整えたので、その時のメモです。基本的には以下の公式サイトのGetting Startedに沿って環境を構築しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://golang.org/doc/install&quot;&gt;Download and install - The Go Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CentOS 7とUbuntu 18.04 LTSの2つのLinuxに開発環境を構築しましたが、同じ手順で構築できました。&lt;/p&gt;
&lt;h2&gt;Goの入手&lt;/h2&gt;
&lt;p&gt;まずはGoのバイナリを入手します。今回はCLIのみのCentOSを使用しているため、wgetで取得します。2019年11月現在は1.13.4が最新バージョンのようなので、これをダウンロードしてきます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ wget https://dl.google.com/go/go1.13.4.linux-amd64.tar.gz

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/usr/local&lt;/code&gt;以下に展開します。公式サイトのコマンドほぼそのままですが、&lt;code&gt;/usr/local&lt;/code&gt;はスーパーユーザーしか書き込み権限がないので、sudoをつけています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo tar -C /usr/local -xzf go1.13.4.linux-amd64.tar.gz

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pathの設定&lt;/h2&gt;
&lt;p&gt;Goの実行ファイルにパスを通します。今回は自分だけgoコマンドが使えればいいので、&lt;code&gt;~/.bash_profile&lt;/code&gt;の最後の行に環境変数の設定を追加します。&lt;br /&gt;
(zshを使っている場合は&lt;code&gt;~/.zprofile&lt;/code&gt;の最後の行に追加してください。)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=$PATH:/usr/local/go/bin

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで一度ログアウトして再度ログインすると、goコマンドが使えるようになります。ログアウトしたくない場合は、&lt;code&gt;source&lt;/code&gt;コマンドで&lt;code&gt;.bash_profile&lt;/code&gt;を再読み込みしてやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ source ~/.bash_profile

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;goコマンドが使えるようになったことを確認します。下記コマンドでgoのバージョンが表示されたら無事Pathが通ってます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go version
go version go1.13.4 linux/amd64

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;動作確認&lt;/h2&gt;
&lt;p&gt;さて、これでGo言語の開発ができるようになったので、HelloWorldを動かしてみます。&lt;code&gt;hello.go&lt;/code&gt;というファイルを作成し、以下のコードを書きます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import &quot;fmt&quot;

func main() {
	fmt.Printf(&quot;hello, world\n&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ビルドして、エラーが出ないことを確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build hello.go

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行します。&lt;code&gt;hello, world&lt;/code&gt;と表示されるのが確認できるかと思います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./hello
hello, world

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、LinuxでGoの開発ができるようになりました。&lt;/p&gt;
&lt;h2&gt;$GOPATHの設定&lt;/h2&gt;
&lt;p&gt;:::caution
最近は&lt;code&gt;$GOPATH&lt;/code&gt;じゃなくてGo Modulesを使用する流れが主流なので、こちらは設定する必要はないかもしれません。
:::&lt;/p&gt;
&lt;p&gt;Go言語では&lt;code&gt;go get&lt;/code&gt;コマンドを使用して簡単に外部ライブラリを導入することができますが、そのためにはGOPATHという環境変数を設定する必要があります。今回は以下を参考に&lt;code&gt;go get&lt;/code&gt;のデフォルトパスらしい&lt;code&gt;$HOME/go&lt;/code&gt;をGOPATHとして指定します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/wiki/SettingGOPATH&quot;&gt;SettingGOPATH · golang/go Wiki · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;~/.bash_profile&lt;/code&gt;の末尾に下記を追記してください。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に&lt;code&gt;.bash_profile&lt;/code&gt;を再読み込みして完了です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ source ~/.bash_profile

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Gitで秘密鍵を指定してリモートリポジトリを操作する</title><link>https://mem-archive.com/posts/git-specification-private-key/</link><guid isPermaLink="true">https://mem-archive.com/posts/git-specification-private-key/</guid><pubDate>Sat, 24 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;githubとbitbucketを併用している場合などは、&lt;code&gt;~/.ssh/config&lt;/code&gt;にドメインごとにどの鍵を使用するか指定するのが一般的だと思います。&lt;br /&gt;
しかし、githubをマルチアカウントで運用するような場合は、ドメインが同じなのでこの方法は使えません。&lt;/p&gt;
&lt;p&gt;前々から不便だなーと思っていたのですが、設定ファイルに頼らずに、リモートリポジトリを操作するときにどの鍵を使用するか指定できることを知ったので、その方法のメモです。&lt;/p&gt;
&lt;h2&gt;gitのバージョン確認&lt;/h2&gt;
&lt;p&gt;今回の方法は、gitのバージョン2.3から使用できるようなので、まずはgitのバージョン確認をします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git --version
git version 2.7.4

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;どうやら対応しているようです。ちなみに環境はWSLなのでUbuntu 16.04です。&lt;/p&gt;
&lt;h2&gt;リモートリポジトリ操作時に鍵を指定する&lt;/h2&gt;
&lt;p&gt;リモートリポジトリを操作するときに鍵を指定するコマンドはこれです。&lt;code&gt;GIT_SSH_COMMAND&lt;/code&gt;という環境変数を使います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ GIT_SSH_COMMAND=&apos;ssh -i 秘密鍵のパス&apos; gitコマンド

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例えば、&lt;code&gt;git clone&lt;/code&gt;時に鍵を指定する場合はこんな感じになり、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ GIT_SSH_COMMAND=&apos;ssh -i ~/.ssh/id_rsa&apos; git clone リモートリポジトリ

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git push&lt;/code&gt;の場合はこんな感じになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ GIT_SSH_COMMAND=&apos;ssh -i ~/.ssh/id_rsa&apos; git push origin master

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;タイプ数は増えるものの自由に鍵を指定できるので、githubとかbitbucketのマルチアカウントの運用もストレスなく使えるようになりました。&lt;/p&gt;
&lt;p&gt;参考サイト：&lt;a href=&quot;https://qiita.com/sonots/items/826b90b085f294f93acf&quot;&gt;git clone 時に秘密鍵を指定する #Git - Qiita&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>CentOSにnginxを導入する</title><link>https://mem-archive.com/posts/2018/11/18/post-1171/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/18/post-1171/</guid><pubDate>Sun, 18 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;勉強がてらCentOS 7.5にnginxを導入したのでそのメモです。&lt;/p&gt;
&lt;h2&gt;nginxをインストール&lt;/h2&gt;
&lt;p&gt;各種ディストリビューションへの導入方法は(英語ですが)以下の公式サイトに書いてあるので、それに従ってnginxを導入します。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/linux_packages.html&quot;&gt;nginx: Linux packages&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;nginxのリポジトリを追加&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;yum&lt;/code&gt;コマンドでnginxをインストールできるように、nginxのリポジトリをCentOSに追加します。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/yum.repos.d/nginx.repo&lt;/code&gt;というファイルを作り、下記を記述します。CentOS 6系を使用している人はbaseurlの7の部分を6にしましょう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;nginxのインストール&lt;/h3&gt;
&lt;p&gt;これで&lt;code&gt;yum&lt;/code&gt;コマンドでnginxをインストールできるようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo yum install nginx

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事インストールされたことを確認できました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nginx -v
nginx version: nginx/1.14.1

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;nginxの起動と動作確認&lt;/h2&gt;
&lt;h3&gt;nginxの起動&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ sudo systemctl start nginx

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちゃんと起動しました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ps aux | grep nginx
root      2696  0.0  0.0  46404   968 ?        Ss   20:09   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     2697  0.0  0.1  46804  1924 ?        S    20:09   0:00 nginx: worker process

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;curlコマンドでの動作確認&lt;/h3&gt;
&lt;p&gt;まずはcurlコマンドで動作確認をします。CentOSから下記コマンドを実行します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ curl localhost

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こんなhtmlのレスポンスが返ってきたら成功です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Welcome to nginx!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;For online documentation and support please refer to
&amp;lt;a href=&quot;http://nginx.org/&quot;&amp;gt;nginx.org&amp;lt;/a&amp;gt;.&amp;lt;br/&amp;gt;
Commercial support is available at
&amp;lt;a href=&quot;http://nginx.com/&quot;&amp;gt;nginx.com&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Thank you for using nginx.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;ブラウザから動作確認&lt;/h3&gt;
&lt;p&gt;CentOS以外の外部のPCのブラウザから動作確認する場合は、&lt;code&gt;http://CentOSサーバのIPアドレス&lt;/code&gt;でnginxの初期ページを表示できたら成功です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./nginx.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;表示できない場合は、CentOSでhttpアクセスを許可していない場合があります。下記コマンドの実行してみて、httpが出力されなかったら許可されていません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo firewall-cmd --list-services --zone=public --permanent
ssh dhcpv6-client http

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;もしhttpがない場合は、httpアクセスを許可してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# httpアクセスを許可
$ sudo firewall-cmd --add-service=http --zone=public --permanent

# 再読み込みして反映
$ sudo systemctl reload firewalld

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、うまく表示できるようになると思います。&lt;/p&gt;
</content:encoded></item><item><title>Linuxのデフォルトshellをzshに変更する</title><link>https://mem-archive.com/posts/2018/11/18/post-1163/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/18/post-1163/</guid><pubDate>Sun, 18 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import MoshimoAffiliate from &quot;@components/misc/MoshimoAffiliate.astro&quot;&lt;/p&gt;
&lt;p&gt;基本的にbashよりzsh派なのですが、毎回デフォルトのshellを変更する方法を忘れるので設定方法を記録に残しておきます。&lt;/p&gt;
&lt;p&gt;今回は、CentOS 7.5を使用します。&lt;/p&gt;
&lt;h2&gt;zshがインストール済みか確認&lt;/h2&gt;
&lt;p&gt;まずはzshがインストール済みかどうか確認です。インストール済みの場合はこのように表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ which zsh
/bin/zsh

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インストールされていない場合はこのように表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ which zsh
/usr/bin/which: no zsh in (/usr/local/bin:/bin/:usr/bin:/usr/local/sbin:/usr/sbin)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;インストールされていない場合は&lt;code&gt;yum&lt;/code&gt;コマンドでインストールします。Debian系のLinuxを使用している場合は&lt;code&gt;apt&lt;/code&gt;コマンドを使ってください。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo yum install zsh

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;デフォルトshellの変更&lt;/h2&gt;
&lt;p&gt;まずは、zshが使用できるかどうか確認します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# -lオプションで使用できるshell一覧を表示。zshが含まれていることを確認
$ chsh -l
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/zsh # &amp;lt;- これ
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ubuntuとかだと&lt;code&gt;-l&lt;/code&gt;オプションがないと怒られる場合があります。その場合、以下で同様のことを確認できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /etc/shells # /etc/shells: valid login shells/bin/sh/bin/bash/bin/rbash/bin/dash/usr/bin/tmux/bin/zsh/usr/bin/zsh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;デフォルトのshellをzshに変更します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# -sオプションでデフォルトshellの種類を変更
$ chsh -s /bin/zsh

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで、次回ログイン時からはzshがデフォルトで起動するようになります。&lt;/p&gt;
&lt;p&gt;現在のデフォルトシェルに何が設定されているかは、こちらをご覧ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2020/02/20/post-2383/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;MoshimoAffiliate data={{
&quot;n&quot;:&quot;LPI Linuxエッセンシャル試験対応 しっかりわかるLinux入門&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/41rHD3v+d9L.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B0C3G7WMWX&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B0C3G7WMWX&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/LPI%20Linux%E3%82%A8%E3%83%83%E3%82%BB%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E8%A9%A6%E9%A8%93%E5%AF%BE%E5%BF%9C%20%E3%81%97%E3%81%A3%E3%81%8B%E3%82%8A%E3%82%8F%E3%81%8B%E3%82%8BLinux%E5%85%A5%E9%96%80/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=LPI%20Linux%E3%82%A8%E3%83%83%E3%82%BB%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E8%A9%A6%E9%A8%93%E5%AF%BE%E5%BF%9C%20%E3%81%97%E3%81%A3%E3%81%8B%E3%82%8A%E3%82%8F%E3%81%8B%E3%82%8BLinux%E5%85%A5%E9%96%80&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;FQpKZ&quot;,&quot;s&quot;:&quot;s&quot;
}} /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>VirtualBoxのホストキーを変更する</title><link>https://mem-archive.com/posts/2018/11/18/post-1151/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/18/post-1151/</guid><pubDate>Sun, 18 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;家ではErgodoxというキーボードを使用しているのですが、VirtualBoxのデフォルトホストキーである右Ctrlキーが存在しなくてハマったので、ホストキーの変更方法のメモです。環境は以下です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;VirtualBox 5.2.22&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ちなみに、ホストキーが存在せず仮想マシンから脱出できなくなった場合、WindowsだったらShiftキーを5回連打して固定キー機能を呼び出せば脱出できました。&lt;/p&gt;
&lt;h2&gt;ホストキーの変更&lt;/h2&gt;
&lt;p&gt;VirtualBoxで&lt;code&gt;ファイル → 環境設定&lt;/code&gt;を選択して、設定画面を開きます。&lt;/p&gt;
&lt;p&gt;そして、&lt;code&gt;入力 -&amp;gt; 仮想マシン&lt;/code&gt;を選択します。ここでホストキーの設定を行います。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./VirtualBox.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ホストキーの組み合わせ&lt;/code&gt;の&lt;code&gt;Right Control&lt;/code&gt;と書いてある部分をクリックして任意のキーを押すと、ホストキーが変更されるので、あとはOKを押せばホストキーの変更は完了です。&lt;/p&gt;
&lt;p&gt;今回は、左Windowsキーにホストキーを割り当てました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./VirtualBox2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;これで、ホストキーが存在しなくて仮想マシンから脱出できない問題が解決されました。&lt;/p&gt;
</content:encoded></item><item><title>OpenCVのcv::viz::writeCloudがis_block_type_valid(header-&gt;_block_use)とか_CrtlsValidHeapPointer(block)で落ちる</title><link>https://mem-archive.com/posts/opencv-is-block-type-valid-error/</link><guid isPermaLink="true">https://mem-archive.com/posts/opencv-is-block-type-valid-error/</guid><pubDate>Sun, 11 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Visual Studio 2017でC++を書いて実行したら何やらエラーで落ちてしまいました。どうにもOpenCVの関数であるcv::viz::writeCloudを呼び出したときに落ちてるらしいことはわかったものの、解決するまでちょっと時間がかかったのでメモしておきます。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;ずっとDebugモードで動かしていたプログラムをReleaseモードで実行すると、このようなエラーが表示されました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./error.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# エラー内容
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 908

Expression: is_block_type_valid(header-&amp;gt;_block_use)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;むむむ。。。&lt;/p&gt;
&lt;p&gt;偶発的に起こるエラーなのか定常的に出るエラーなのか調査するために何度か実行していると、違うエラーメッセージも表示されました。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./error2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# エラー内容
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 904

Expression: _CrtIsValidHeapPointer(block)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ふむむ。。。なんかヒープが破壊されているのか？なんかメモリ周りのエラーっぽいです。&lt;/p&gt;
&lt;p&gt;エラーメッセージでググってみると、異なるバージョンのVisual Studioでビルドしたライブラリを使用したり、古いVCで作ったプロジェクトを新しいVCにアップグレードした場合、このエラーが発生することがあるようです。&lt;/p&gt;
&lt;p&gt;参考: &lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/6e2fbab3-1336-421d-9d33-07b7d3a5e0ad/crtisvalidheappointerblock-exception-on-load?forum=vclanguage&quot;&gt;_CrtIsValidHeapPointer(block) exception on load | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ただ、このPCはVC 2017以外入れたことないので、VCのバージョンの違いが原因ではなさそうです。&lt;/p&gt;
&lt;p&gt;あとはアドレスの位置ずれが原因でも発生するようですが、基本スマートポインタとvectorくらいしか使っていなかったので、その可能性も薄そうです。&lt;/p&gt;
&lt;p&gt;参考: &lt;a href=&quot;https://so-zou.jp/software/tech/programming/cpp/object-oriented/class/&quot;&gt;クラス | C++ プログラミング解説&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;原因を切り分けていくとどうにもOpenCVのcv::viz::writeCloudを呼び出しているときに落ちている模様。&lt;br /&gt;
原因特定を進めていき、cv::viz::writeCloudに入力している引数たちの中身やサイズは問題なさそうなので、cv::viz::writeCloudの中で良からぬことが起きていそうだという可能性が浮上しました。&lt;/p&gt;
&lt;h2&gt;解決策&lt;/h2&gt;
&lt;p&gt;結論から言うと、OpenCVとVTKのバージョンを最新にすると解決しました。&lt;/p&gt;
&lt;p&gt;今まで使用していたのは&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenCV 3.4.0&lt;/li&gt;
&lt;li&gt;VTK 8.1.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;だったのですが、これを最新安定版の&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OpenCV 3.4.3&lt;/li&gt;
&lt;li&gt;VTK 8.1.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;を使うようにしたら、上記のエラーは起こらなくなりました。&lt;/p&gt;
&lt;p&gt;結局根本的な原因まではわからなかったのですが、ひとまず動くようになったためよしとしました。もしVTKとかOpenCVの不具合だったとしても、issueを立てるくらいしかできそうにないので・・・&lt;/p&gt;
</content:encoded></item><item><title>【OpenCV 3.4】SIFTを使おうとするとError: The function/feature is not implementedと出力された</title><link>https://mem-archive.com/posts/2018/11/10/post-1129/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/10/post-1129/</guid><pubDate>Sat, 10 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今までOpenCV 3.4.0を使用していたのですが、最近OpenCV 3.4.3にバージョンアップしました。バージョンアップ後にSIFTを使おうとするとちょっとハマったのでメモです。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;SIFTを使いたかったので、OpenCVのopencv_contribも一緒にビルドしました。しかし、いざSIFTを使ったプログラムを動かそうとすると、こんなエラーが表示され、SIFTを使えませんでした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(3.4.3) Error: The function/feature is not implemented (This algorithm is patented and is excluded in this configuration; Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library) in cv::xfeatures2d::SIFT::create, file D:\library\opencv-3.4.3\opencv_contrib-3.4.3\modules\xfeatures2d\src\sift.cpp, line 1207

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特許で保護された機能を使うには、それ専用のオプションをつけてビルドしてね。ということらしいです。&lt;/p&gt;
&lt;p&gt;SIFTは特許で保護されているので使えなかったわけですね。&lt;/p&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;p&gt;出力されたエラーを見るに、CMakeでOPENCV_ENABLE_NONFREEというオプションをつけて再度OpenCVをビルドすればいけそうな雰囲気です。CMakeのオプションを探せば確かにありましたこのオプション。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./CMake.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;このオプションにチェックをつけてOpenCVをビルドし直します。そして再度SIFTを使ったプログラムをビルド&amp;amp;実行すると、無事にSIFTが動きました！！&lt;/p&gt;
</content:encoded></item><item><title>Visual C++でM_PI(円周率)を使う</title><link>https://mem-archive.com/posts/2018/11/04/post-1088/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/04/post-1088/</guid><pubDate>Sun, 04 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Visual C++で円周率の定義&lt;code&gt;M_PI&lt;/code&gt;などを使う場合、gccなどと同じ風に使おうとすると&lt;code&gt;M_PI&lt;/code&gt;が見つからないと怒られてしまいます。&lt;/p&gt;
&lt;p&gt;毎回Visual C++の場合どうやって書くんだっけなーとググって解決しているので、対応方法をまとめておこうかと思います。環境はVisual Studio 2017になります。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;Visual C++で&lt;code&gt;M_PI&lt;/code&gt;を使いたい場合、gccなどと同じように&lt;code&gt;cmath&lt;/code&gt;をincludeしただけでは下記のようなエラーが出てコンパイルが通りません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; error C2065: &apos;M_PI&apos;: 定義されていない識別子です。

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;p&gt;基本的には&lt;code&gt;cmath&lt;/code&gt;をincludeする前に、&lt;code&gt;_USE_MATH_DEFINES&lt;/code&gt;をdefineしてやればコンパイルが通るようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _USE_MATH_DEFINES  
#include &amp;lt;cmath&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考： &lt;a href=&quot;https://msdn.microsoft.com/ja-jp/library/4hwaceh6.aspx&quot;&gt;Math Constants | Microsoft Learn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;注意点としては、&lt;strong&gt;他のヘッダファイルのincludeよりも前に&lt;code&gt;_USE_MATH_DEFINES&lt;/code&gt;の定義を書きましょう&lt;/strong&gt;。そうでないと、他のヘッダファイルが間接的にcmathをinluceしていた場合、エラーが解決しないので・・・。&lt;/p&gt;
&lt;p&gt;この辺の詳しい原因はこちらのブログで詳しく解説してあり、非常に参考になりました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://louis-needless.hatenablog.com/entry/cannot-use-m_pi&quot;&gt;定数M_PIが使えない - 需要のないページ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C++を独自に拡張するのは別にいいのですが、まずは標準のC++の仕様を満たした上で拡張してほしいものですね。頼むぞMicrosoft。&lt;/p&gt;
</content:encoded></item><item><title>多視点三角測量(Multi view triangulation)のアルゴリズム</title><link>https://mem-archive.com/posts/2018/11/04/post-867/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/11/04/post-867/</guid><pubDate>Sat, 03 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import MoshimoAffiliate from &quot;@components/misc/MoshimoAffiliate.astro&quot;
import img1 from &quot;./perspective_projection.jpg&quot;
import img2 from &quot;./perspective_projection6.jpg&quot;
import img3 from &quot;./perspective_projection2.jpg&quot;
import img4 from &quot;./perspective_projection3-1.jpg&quot;
import img5 from &quot;./perspective_projection4-1.jpg&quot;
import img6 from &quot;./perspective_projection5.jpg&quot;
import img7 from &quot;./perspective_projection7.jpg&quot;
import img8 from &quot;./perspective_projection8.jpg&quot;
import img9 from &quot;./perspective_projection9-1.jpg&quot;&lt;/p&gt;
&lt;p&gt;最近知ったのですが、OpenCVにもいつの間にか多視点三角測量(Multi view triangulation)が実装されていたんですね。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.opencv.org/5.0/main_modules/d_projection.html#triangulatepoints&quot;&gt;3D vision functionality — OpenCV Tutorials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ということで今回は、OpenCVの&lt;code&gt;cv::sfm::triangulatePoints&lt;/code&gt;でも使われている、古典的なMulti view triangulationのアルゴリズムについて解説してみます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;triangulatePoints&lt;/code&gt;はこの書籍のアルゴリズムを実装してます。この本は昔ながらの有用な3次元系のアルゴリズムを網羅しているため、英語が苦にならない人にはお勧めです。&lt;/p&gt;
&lt;p&gt;&amp;lt;MoshimoAffiliate data={{
&quot;n&quot;:&quot;Multiple View Geometry in Computer Vision&quot;,&quot;b&quot;:&quot;Cambridge University Press&quot;,&quot;t&quot;:&quot;10232097&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/410TxmdT9qL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/0521540518&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/0521540518&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/Multiple%20View%20Geometry%20in%20Computer%20Vision/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=Multiple%20View%20Geometry%20in%20Computer%20Vision&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;wOSQC&quot;,&quot;s&quot;:&quot;s&quot;
}} /&amp;gt;&lt;/p&gt;
&lt;h2&gt;Multi view triangulationとは&lt;/h2&gt;
&lt;p&gt;複数の画像の特徴点の対応付けと透視投影変換行列から、その特徴点の3次元座標を求めるのがMulti view triangulationです。すなわち、下記の式を満たす世界座標点 $\boldsymbol{X}_w=[X_w, Y_w, Z_w, 1]^T$ を見つけるわけです。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;数式&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;実際には諸々誤差の影響ですべての画像でこの式をぴったり満たす世界座標点は見つからないので、なるべく満たしそうな世界座標点を線形的に見つけてやります。&lt;/p&gt;
&lt;p&gt;基本的な考え方としては下図のように、再投影誤差(画像座標点と世界座標点を画像に投影した点の距離)が最小になるような世界座標点が、求めたい3次元点だろうという考えです。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;イメージ図&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Multi view triangulationのアルゴリズム&lt;/h2&gt;
&lt;p&gt;では実際に世界座標点の導出法について説明していきます。まず、上の透視投影変換の式を展開するとこのようになります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;表記を簡単にするために、透視投影変換行列の各行を$\boldsymbol{p}_1^T, \boldsymbol{p}_2^T, \boldsymbol{p}_3^T$とおきます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;数式&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;すると先ほどの式はこうなりますね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img5.src}
alt=&quot;数式&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ここで、(3)&apos;を(1)&apos;と(2)&apos;に代入して $s$ を消して整理してやります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img6.src}
alt=&quot;数式&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;特徴点の対応付けは複数枚の画像でわかっているはずですので、実際にはこの式は画像数分成り立ちます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img7.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;これを行列式で表すと&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img8.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;という風になります。実際は $[X_w, Y_w, Z_w, 1]^T$ を定数倍してもこの式は成り立つため、スケールの要素を加味して、&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img9.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;とおいてやります。 $S[X_w, Y_w, Z_w, 1]^T = [X&apos;_w, Y&apos;_w, Z&apos;_w, S]^T$ とおいたわけですね。&lt;/p&gt;
&lt;p&gt;さて、あとはこの行列の最小特異値に対応する特異値ベクトルを求めることで、一番それらしい $[X&apos;_w, Y&apos;_w, Z&apos;_w, S]^T$ が求まります。&lt;/p&gt;
&lt;p&gt;最後に、スケール要素を削除して $[X_w, Y_w, Z_w] = [X&apos;_w/S, Y&apos;_w/S, Z&apos;_w/S]^T$ としてやります。これで、特徴点の対応付けから世界座標点を求めることができました。&lt;/p&gt;
</content:encoded></item><item><title>pip install --upgrade pipでpipのバージョンを上げるとImportError: cannot import name mainで動かなくなった話</title><link>https://mem-archive.com/posts/2018/10/28/post-996/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/28/post-996/</guid><pubDate>Sun, 28 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;aptでpipをインストールした後にpipのバージョンアップを試みると、pipが動かなくなってしまったのでその対策をまとめます。&lt;/p&gt;
&lt;h2&gt;現象&lt;/h2&gt;
&lt;p&gt;WSL (Windows Subsysmte for Linux)、つまりUbuntu 16.04でpipのバージョン9以上を使いたかったのです。でもaptでインストールされたのはpip 8.1.1だったのでpipをアップグレードをしたら、pipが壊れて動かなくなりました。&lt;/p&gt;
&lt;h3&gt;やったこと&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# pipのインストール
$ sudo apt install python-pip

# バージョン確認
$ pip --version
pip 8.1.1 from /usr/lib/python2.7/dist-packages (python 2.7)

# pip 9以上を使いたかったのでのバージョンアップ
$ pip install --upgrade pip

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;起きたエラー&lt;/h3&gt;
&lt;p&gt;pipコマンドが壊れて、pipで何かやろうとするとこのエラーが出るようになってしまいました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pip
Traceback (most recent call last):
  File &quot;/usr/bin/pip&quot;, line 9, in &amp;lt;module&amp;gt;
    from pip import main
ImportError: cannot import name main

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なぜこのエラーが起きるかの原因については、こちらのサイトがわかりやすかったです。どうにもpip 10からディレクトリ構造など諸々変わってしまったのが原因みたい・・・。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://icchy.hatenablog.jp/entry/2018/04/17/064443&quot;&gt;pip install --upgrade pip (10.0.0) 後の奇妙な挙動について - 雑記&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;h3&gt;pipのアンインストール&lt;/h3&gt;
&lt;p&gt;とりあえずpipを一旦アンインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# pipのアンインストール
$ sudo apt --purge remove python-pip

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このままもう1回aptでpipをインストールしても動かなくなってしまっているので、pipを完全にアンインストールしてやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python -m pip uninstall pip

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでpipインストール前の綺麗な状態に戻りました。&lt;/p&gt;
&lt;h3&gt;aptを使わずにpipをインストール&lt;/h3&gt;
&lt;p&gt;aptのpipパッケージが新しくなればこのアップデートで動かなくなる問題も解決するかもしれませんが、今回はaptを使ったpipのインストールは諦めて、使わない方法に切り替えます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# aptを使わずpipのインストール
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ sudo python get-pip.py

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでpipがインストールされました。バージョンを確認すると、目的のpip 9以上がインストールされているのが確認できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ pip --version
pip 18.1 from /usr/local/lib/python2.7/dist-packages/pip (python 2.7)

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>カメラ座標系から世界座標系への変換 ～外部パラメータの逆変換～</title><link>https://mem-archive.com/posts/2018/10/28/post-962/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/28/post-962/</guid><pubDate>Sat, 27 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./CV-1.jpg&quot;
import img2 from &quot;./スライド3-1.jpg&quot;
import img3 from &quot;./extrinsic.jpg&quot;
import img4 from &quot;./extrinsic3.jpg&quot;
import img5 from &quot;./extrinsic5.jpg&quot;
import img6 from &quot;./extrinsic4.jpg&quot;
import img7 from &quot;./extrinsic6-1.jpg&quot;&lt;/p&gt;
&lt;p&gt;外部パラメータは、世界座標系からカメラ座標系へ変換する行列でした。今回は、この外部パラメータの逆変換、すなわちカメラ座標系から世界座標系への変換を考えてみます。&lt;/p&gt;
&lt;h2&gt;世界座標系からカメラ座標系への変換&lt;/h2&gt;
&lt;p&gt;そもそも外部パラメータについて知りたい人は以下で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;https://mem-archive.com/2018/02/17/post-74/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;まずは復習ですが、外部パラメータを使って世界座標系からカメラ座標系へ変換はこのように行いました。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;そして、この変換は同次座標系で表された変換でした。同次座標系を使わない場合の変換はこちらになります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ちょっとこのままでは使いにくいので、回転行列を$\boldsymbol{R}$、並進ベクトルを$\boldsymbol{t}$とおきます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;数式&quot;
style=&quot;width: 400px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;この同次座標を使わない形を覚えておいてください。&lt;/p&gt;
&lt;h2&gt;カメラ座標系から世界座標系への変換&lt;/h2&gt;
&lt;p&gt;さて、ではいよいよ外部パラメータの逆変換を求めてみましょう。まずはカメラ座標系の原点を世界座標系原点と一致させるように移動させてやります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;式で表すとこうなります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img5.src}
alt=&quot;数式&quot;
style=&quot;width: 400px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;さらに、カメラ座標系の各座標軸を世界座標系と合うように回転させてやります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img6.src}
alt=&quot;数式&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;つまり式で表すとこうですね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img7.src}
alt=&quot;数式&quot;
style=&quot;width: 400px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;これで&lt;strong&gt;カメラ座標点から世界座標点へ変換する式&lt;/strong&gt;を導出できました。これで任意の3次元点をカメラ座標系から世界座標系へ変換することができますね。&lt;/p&gt;
&lt;p&gt;個人的にはこの変換は、世界座標系でみたカメラ位置の導出によく使います。すなわち$[X_c, Y_c, Z_c]^T=[0, 0, 0]^T$の場合です。&lt;/p&gt;
</content:encoded></item><item><title>scrapbox-converterの使い方 ～EvernoteからScrapboxへの移行～</title><link>https://mem-archive.com/posts/2018/10/14/post-821/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/14/post-821/</guid><pubDate>Sun, 14 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;EvernoteからScrapboxに移行するときに、&lt;strong&gt;scrapbox-converter&lt;/strong&gt;というツールを使えば簡単に移行できるので、その使い方をまとめておきます。&lt;/p&gt;
&lt;p&gt;今回は、Windows10の&lt;strong&gt;Windows Subsystem for Linux (WSL)&lt;/strong&gt;、すなわち&lt;strong&gt;Bash on Windows&lt;/strong&gt;上でscrapbox-converterを動かします。&lt;/p&gt;
&lt;p&gt;※ 記事の最後にも書いてありますが、ありがたいツールではあるものの、現状まだまだな雰囲気を感じるツールです。うまく変換できたらラッキーくらいで試してみるのがいいと思います。&lt;br /&gt;
※ どうやらもう少ししたらEvernote XMLからの変換精度がよくなる模様(参考：&lt;a href=&quot;https://scrapbox.io/pastak-pub/%E5%83%95%E3%81%AE%E4%BD%9C%E3%81%A3%E3%81%9FScrapbox%E9%96%A2%E9%80%A3%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E7%B4%B9%E4%BB%8B&quot;&gt;僕の作ったScrapbox関連ツールの紹介&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;npmのインストール&lt;/h2&gt;
&lt;p&gt;まずはBashを開いてにnpmコマンドをインストールします。すでにインストール済みの場合はこの手順は飛ばしてください。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# nodejsとnpmをインストール
$ sudo apt update 
$ sudo apt install nodejs npm

# aptコマンドでインストールされるnodejsとnpmはバージョンが古いので、念のため最新版をインストール
$ npm --version
3.5.2 # バージョンが古いことを確認
$ sudo npm cache clean 
$ sudo npm install n -g
$ npm --version
6.4.1 # バージョンが新しくなったことを確認
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://qiita.com/seibe/items/36cef7df85fe2cefa3ea&quot;&gt;Ubuntuに最新のNode.jsを難なくインストールする&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;scrapbox-converterのインストール&lt;/h2&gt;
&lt;p&gt;npmをインストールしたら、scrapbox-converterをインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo npm install -g scrapbox-converter
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;GyazoのAPIトークンを取得&lt;/h2&gt;
&lt;p&gt;Evernote XML (.exml)からjsonへ変換する場合、画像はGyazoにアップロードされるようなので、&lt;a href=&quot;https://gyazo.com/ja&quot;&gt;Gyazo&lt;/a&gt;のアカウントを持っていないなら作成しましょう。さらにscrapbox-converterで変換する場合、GyazoのアクセスIトークンが必要になるので、こちらから取得しましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gyazo.com/api&quot;&gt;Gyazo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;APIトークンを作成する場合、こんな画面に行き着くと思いますが、Nameに任意の文字列、Callback URLの任意のURLを入力します。どうやらCallback URLはURLの形をしてたら存在しないアドレスでも大丈夫っぽいです。(今回は適当に&lt;code&gt;http://piyopiyo.com&lt;/code&gt;にしましたw)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./gyazo-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;トークンを作成したら、トークンの詳細からAccess TokenをGenerateできると思うので、Generateしてやります。すると、Your access tokenという部分に文字列が表示されるので、この文字列を控えておきましょう。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./gyazo2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Evernote XML (.exml)からjsonファイルへ変換&lt;/h2&gt;
&lt;p&gt;さて、いよいよscrapbox-converterを動かしましょう。まずは、環境変数にトークンを設定します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# アクセストークンを見つけられるようにする
$ export GYAZO_ACCESS_TOKEN=&quot;アクセストークンの文字列&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そして、exmlファイルをjsonへ変換します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# exmlからjsonへ変換。WSLのBashでは/mnt以下にCドライブとかDドライブがマウントされてる風になってる
$ scrapbox-converter /mnt/c/tmp/hogehoge.enex &amp;gt; /mnt/c/tmp/import.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでScrapboxでインポートできる形式になりました。&lt;/p&gt;
&lt;p&gt;参考：&lt;a href=&quot;https://github.com/pastak/scrapbox-converter&quot;&gt;GitHub - pastak/scrapbox-converter: import scrapbox from markdown|html|enex files · GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Scrapboxでインポート&lt;/h2&gt;
&lt;p&gt;作成したjsonファイルを、ScrapboxのSetting -&amp;gt; Page Data -&amp;gt; Import Pagesから読み込みます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./clip5_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;するとScrapboxにjsonに含まれるページがインポートされます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./clip6_1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;感想&lt;/h2&gt;
&lt;p&gt;このscrapbox-converter、Evernote XMLを一気にjsonに変換できて便利なんですけど、レイアウトの精度はいまいちな気がします。あとは、エラーが出力されて変換できないこともあったので、今後に期待ですかね。&lt;/p&gt;
&lt;p&gt;きれいに移行したいなら、1件1件手作業で移行させるのが今のところ一番なんですかね。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;Scrapbox情報整理術&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/51yMZ+QU40L.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B07GJFBWWZ&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B07GJFBWWZ&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/Scrapbox%E6%83%85%E5%A0%B1%E6%95%B4%E7%90%86%E8%A1%93/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=Scrapbox%E6%83%85%E5%A0%B1%E6%95%B4%E7%90%86%E8%A1%93&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;3cuY5&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-3cuY5&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>画像座標系からカメラ座標系への変換</title><link>https://mem-archive.com/posts/2018/10/13/post-682/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/13/post-682/</guid><pubDate>Sat, 13 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./image2camera-1.jpg&quot;
import img2 from &quot;./キャプチャ2.png&quot;
import img3 from &quot;./image2camera2.jpg&quot;
import img4 from &quot;./image2camera3-1.jpg&quot;&lt;/p&gt;
&lt;p&gt;画像座標点をカメラ座標点に変換することを考えてみます。&lt;/p&gt;
&lt;p&gt;タイトル詐欺で申し訳ないですが、結論から言うと&lt;strong&gt;画像座標点をカメラ座標点への変換はできません&lt;/strong&gt;。できるのは&lt;strong&gt;カメラ座標系原点からカメラ座標点方向へのベクトルを計算すること&lt;/strong&gt;だけです。&lt;/p&gt;
&lt;p&gt;まぁ、2次元から3次元への変換なんて、明らかに奥行きの情報が足りてないから当然ですよね。なので今回は、このベクトル変換方法についてまとめてみます。&lt;/p&gt;
&lt;h2&gt;画像座標点からカメラ座標ベクトルへの変換&lt;/h2&gt;
&lt;p&gt;内部パラメータが既知の場合、カメラ座標系原点からカメラ座標点方向を計算できます。&lt;/p&gt;
&lt;p&gt;これを説明しやすくするためカメラ座標ベクトルと呼ぶことにします。下の図の赤のベクトルのことですね。&lt;/p&gt;
&lt;p&gt;このカメラ座標ベクトルの求め方を今回はまとめてみます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;イメージ図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;カメラ座標ベクトルとか画像座標点に内部パラメータの逆行列を掛ければいいんでしょ？とか思ってるそこのあなた！！逆行列の計算コストはそれなりに高いです。&lt;/p&gt;
&lt;p&gt;そんなことしなくてもカメラ座標ベクトルは求められます。この辺の話は、内部パラメータの意味を理解してればわかると思います。&lt;/p&gt;
&lt;p&gt;内部パラメータの意味を知らない人はまずはこちらを理解しましょう。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/21/post-157/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;それでは、カメラ座標ベクトルの求め方を順を追って説明します。&lt;/p&gt;
&lt;p&gt;なお、内部パラメータは例によってこれで表記します。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;数式&quot;
style=&quot;width: 200px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h3&gt;1. 画像座標系原点を移動させる&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;イメージ図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;まずは考えやすくするため、カメラ座標系のZ軸上に画像座標系原点があると考えます。&lt;/p&gt;
&lt;p&gt;すなわち、光学中心の$(u,v)$分画像座標系原点を移動させるので、画像座標系$(u,v)$は移動後$(u-c_x,v-c_y)$となります。&lt;/p&gt;
&lt;h3&gt;2. カメラ座標ベクトルを求める&lt;/h3&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;イメージ図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;さて、ここで内部パラメータの意味を思い出してほしいのですが、カメラ座標点を焦点距離$f$の位置に集めたものが画像座標系でした。&lt;/p&gt;
&lt;p&gt;なので、画像座標点の奥行は$f$ということになるので、$(u-c_x, v-c_y,f)$となります。&lt;/p&gt;
&lt;p&gt;ですので、&lt;strong&gt;カメラ座標ベクトルは$(u-c_x, v-c_y,f)$となります。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$f_x$と$f_y$の値が異なる場合は、$(\frac{u-c_x}{f_x},\frac{v-c_y}{f_y},1)$とした方が考えやすいかもです。&lt;/p&gt;
</content:encoded></item><item><title>Linuxで再帰的に特定の拡張子のファイルのみ削除</title><link>https://mem-archive.com/posts/2018/10/13/post-774/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/13/post-774/</guid><pubDate>Sat, 13 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;特定のディレクトリ内の、指定した拡張子のファイルを再帰的に削除するコマンドをメモとして残しておきます。&lt;/p&gt;
&lt;h2&gt;基本コマンド&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 例としてディレクトリ内のテキストファイルを再帰的に削除するコマンド
$ find ディレクトリパス -name &quot;*.txt&quot; | xargs rm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;意味としてはコマンドのまんまなのですが、一応コマンドの内容を解説しておきます。このコマンドは以下の手順で再帰的に特定の拡張子のファイルを削除しています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;findコマンドでディレクトリ内の特定の拡張子のファイルパスを再帰的に探索&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;探索したファイルパスをrmコマンドに渡して削除&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;ディレクトリ名に拡張子が含まれている場合&lt;/h2&gt;
&lt;p&gt;上記のコマンドだと(滅多にないと思いますが)、hoge.txtみたいな名前のディレクトリが存在してた場合はファイルの削除は成功するものの、下記のようなエラーが表示されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# hoge.txtはディレクトリのため、オプションなしのrmコマンドでは削除に失敗する
$ find hoge -name &quot;*.txt&quot; | xargs rm
rm: cannot remove &apos;hoge/hoge.txt&apos;: Is a directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この場合はちょっとタイプ数は増えますが、下記コマンドを使いましょう(エラーが気にならないなら別に使わなくてもいいです)。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ find ディレクトリパス -name &quot;*.txt&quot; -type f | xargs rm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;findコマンドのtype -fオプションは、探索するのをファイルのみに制限します。&lt;/p&gt;
&lt;p&gt;このオプションをつけないとファイルだろうがディレクトリだろうが、一致する名前のものをすべて探索しちゃうので、ディレクトリも削除しようとしてしまい、エラーが出力されるわけです。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;ITエンジニア1年生のための まんがでわかるLinux コマンド＆シェルスクリプト基礎編&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/51jflVQho1L.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B09XM7PL4P&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B09XM7PL4P&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/IT%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A21%E5%B9%B4%E7%94%9F%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%20%E3%81%BE%E3%82%93%E3%81%8C%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8BLinux%20%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%EF%BC%86%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%9F%BA%E7%A4%8E%E7%B7%A8/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=IT%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A21%E5%B9%B4%E7%94%9F%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%20%E3%81%BE%E3%82%93%E3%81%8C%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8BLinux%20%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%EF%BC%86%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%9F%BA%E7%A4%8E%E7%B7%A8&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;7YXPr&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-7YXPr&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>The Android Gradle plugin supports only Kotlin Gradle plugin version 1.2.51 and higher ～Kotlin Gradle pluginのバージョンが古いと怒られた話～</title><link>https://mem-archive.com/posts/2018/10/08/post-706/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/10/08/post-706/</guid><pubDate>Mon, 08 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;エラー&lt;/h2&gt;
&lt;p&gt;久しぶりにAndroid Studioを起動して、促されるままに諸々バージョンアップしたら、↓↓のように怒られて作ってたアプリのbuildが失敗するようになってしまいました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The Android Gradle plugin supports only Kotlin Gradle plugin version 1.2.51 and higher. Project &apos;hogehoge&apos; is using version 1.1.51.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そのうちまた出会いそうな予感がするので、対応メモを残しておこうと思います。&lt;/p&gt;
&lt;h2&gt;対策&lt;/h2&gt;
&lt;p&gt;これが作ってるアプリのbuild.gradleです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;buildscript {
    ext.kotlin_version = &apos;1.1.51&apos; // ここを修正
    repositories {
        jcenter()

        maven {
            url &apos;https://maven.google.com&apos;
        }
        google()
    }
    dependencies {
        classpath &apos;com.android.tools.build:gradle:3.2.0&apos;
        classpath &quot;org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version&quot;

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2行目の&lt;code&gt;ext.kotlin_version = &apos;1.1.51&apos;&lt;/code&gt;のバージョン部分を1.2.51に変更するだけです。簡単ですね。&lt;/p&gt;
</content:encoded></item><item><title>Ceres Solverのビルド on Windows10 + Visual Studio 2017</title><link>https://mem-archive.com/posts/2018/09/30/post-633/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/09/30/post-633/</guid><pubDate>Sun, 30 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ceres SolverはGoogle謹製の非線形最小二乗問題のソルバーライブラリです。&lt;br /&gt;
とても便利に使わせてもらってるのですが、毎回ビルドに躓くので、ビルド手順をメモっておきます。&lt;br /&gt;
今回はCXSparseもSuiteSparseもすべて使った、盛り盛りの状態でビルドしてみようと思います。&lt;/p&gt;
&lt;h2&gt;開発環境&lt;/h2&gt;
&lt;p&gt;私の開発環境はこんな感じです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10&lt;/li&gt;
&lt;li&gt;CMake 3.10.0&lt;/li&gt;
&lt;li&gt;Visual Studio 2017&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;依存ライブラリの入手 &amp;amp; ビルド&lt;/h2&gt;
&lt;p&gt;Ceres Solverは色々と依存ライブラリがあるので、まずは各種依存ライブラリを入手&amp;amp;ビルドします。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Eigen&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;http://eigen.tuxfamily.org/index.php?title=Main_Page&quot;&gt;公式サイト&lt;/a&gt;から最新安定版(現時点Eigen 3.3.5)をダウンロードします。Eigenはテンプレートライブラリなので、ビルドは不要です。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gflags&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/gflags/gflags/releases&quot;&gt;GitHubのリポジトリ&lt;/a&gt;から最新安定版(現時点gflags 2.2.1)をダウンロードして展開 &amp;amp; CMakeでVisual Studio用のプロジェクトファイルを作ってビルドします。特に問題なくビルドできると思います。 &lt;code&gt;CMAKE_INSTALL_PREFIX&lt;/code&gt;は適時適当な場所に設定してください。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;glog&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/google/glog/releases&quot;&gt;GitHubのリポジトリ&lt;/a&gt;から最新安定版(現時点glog 0.3.5)をダウンロードして展開 &amp;amp; CMakeとVSでビルドします。&lt;br /&gt;
&lt;code&gt;CMAKE_INSTALL_PREFIX&lt;/code&gt;は適時適当な場所に設定してください。&lt;br /&gt;
CMakeのビルドオプションに&lt;code&gt;gflags_DIR&lt;/code&gt;があるので、せっかくなのでgflagsのビルドディレクトリを指定しましょう。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SuiteSparse&lt;/strong&gt;&lt;br /&gt;
公式のSuiteSparseはWindowsをサポートしていません。&lt;br /&gt;
公式サイトにはWindows使ってる人は&lt;a href=&quot;https://github.com/jlblancoc/suitesparse-metis-for-windows/releases&quot;&gt;suitesparse-metis-for-windows&lt;/a&gt;使ってみてねと書いてあったので、これの最新安定版(現時 v1.4.0)をダウンロードします。CMakeは初期状態のままで問題なくビルドできると思います。&lt;br /&gt;
&lt;code&gt;SUITESPARSE_INSTALL_PREFIX&lt;/code&gt;は適時適当な場所に設定してください。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;intel TBB&lt;/strong&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.threadingbuildingblocks.org/download#stable-releases&quot;&gt;公式サイト&lt;/a&gt;から最新安定版(現時点Threading Building Blocks 2019)をダウンロードします。ビルド済みのバイナリが配られているので、今回はそれを使います。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Ceres Solverのビルド&lt;/h2&gt;
&lt;p&gt;最新安定版のCeres Solver(現時点1.14.0)を下記サイトの「latest stable release」といういうリンクから入手します。&lt;br /&gt;
git cloneで入手してもよいですが、手に入るのは最新の開発版で安定していない可能性があるので、特にこだわりがなければ最新安定板の方を入手しましょう。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://ceres-solver.org/installation.html&quot;&gt;Installation — Ceres Solver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;まずはそのままCMakeのConfigureを実行してみます。CMakeのAdvancedにチェックを入れておいてください。&lt;code&gt;EIGEN_INCLUDE_DIR&lt;/code&gt;が設定されてないよ！と怒られるので、&lt;code&gt;EIGEN_INCLUDE_DIR&lt;/code&gt;に&lt;code&gt;EIGEN_ROOT&lt;/code&gt;を設定してもう一回実行します。&lt;code&gt;EIGEN_ROOT&lt;/code&gt;はEigenのルートディレクトリです。&lt;/li&gt;
&lt;li&gt;一応CMakeの実行でエラーが出なくなると思います。もし&lt;code&gt;glog_DIR&lt;/code&gt;がないと怒られたらglogのビルドディレクトリを、&lt;code&gt;gflags_DIR&lt;/code&gt;がないと怒られたらgflagsのビルドディレクトリを指定してCMakeを実行してください。&lt;/li&gt;
&lt;li&gt;blasの設定をします。便宜上CMakeの&lt;code&gt;SUITESPARSE_INSTALL_PREFIX&lt;/code&gt;で設定したディレクトリを&lt;code&gt;SuiteSparse_INSTALL&lt;/code&gt;と表しています。下記を設定してConfigureを実行します。
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;BLAS_blas_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib64/lapack_blas_windows/libblas.lib&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;lapackを設定します。下記を設定してConfigureを実行します。
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;LAPACK_lapack_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib64/lapack_blas_windows/liblapack.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LAPACK&lt;/code&gt;にチェックを入れる&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;SuiteSparse周りのCMakeオプションを設定します。下記を設定してConfigureを実行します。
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;AMD_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AMD_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libamd.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAMD_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAMD_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libcamd.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCOLAMD_INCLUDE_DIR&lt;/code&gt;: &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CCOLAMD_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libccolamd.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CHOLMOD_INCLUDE_DIR&lt;/code&gt;: &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CHOLMOD_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libcholmod.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;COLAMD_INCLUDE_DIR&lt;/code&gt;: &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;COLAMD_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libcolamd.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUITESPARSEQR_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUITESPARSEQR_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libspqr.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUITESPARSE_CONFIG_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUITESPARSE_CONFIG_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/suitesparseconfig.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;METIS_LIBRARY&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/metis.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUITESPARSE&lt;/code&gt; にチェックを入れる&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;CXSparse周りのCMakeオプションを設定します。
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;CXSparse_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/include/suitesparse&lt;/code&gt;を設定してConfigure&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CXSparse&lt;/code&gt;にチェックを入れる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CXSPARSE_INCLUDE_DIR&lt;/code&gt; : &lt;code&gt;SuiteSparse_INSTALL/lib/libcxsparse.lib&lt;/code&gt;を設定してConfigure&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;intel TBB周りのCMakeオプションを設定します。便宜上TBBのダウンロードディレクトリを&lt;code&gt;TBB_DIR&lt;/code&gt;と表記します。下記を設定してConfigureを実行します。
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;TBB_INCLUDE_DIRS&lt;/code&gt; : &lt;code&gt;TBB_DIR/include&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbb_LIBRARY_DEBUG&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbb_debug.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbb_LIBRARY_RELEASE&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbb.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbb_preview_LIBRARY_DEBUG&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbb_preview_debug.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbb_preview_LIBRARY_RELEASE&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbb_preview.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbbmalloc_LIBRARY_DEBUG&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbbmalloc_debug.lib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TBB_tbbmalloc_LIBRARY_RELEASE&lt;/code&gt; : &lt;code&gt;TBB_DIR/lib/intel64/vc14_uwp/tbbmalloc.lib&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;CMakeのGenerateを実行。これでようやくVisual Studio 2017のプロジェクトファイルが出来上がります。&lt;/li&gt;
&lt;li&gt;最後にVisual Studio 2017でビルドしてやればCeres Solverのビルド完了です！！&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>OpenCVのfindEssentialMatでの内部パラメータの用途</title><link>https://mem-archive.com/posts/2018/09/17/post-615/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/09/17/post-615/</guid><pubDate>Mon, 17 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;5-point algorithm (5点アルゴリズム)を使いたくて、OpenCVに確か実装されてたよなーって思って調べてみると、なぜか関数の入力に内部パラメータが必要とされていました。&lt;/p&gt;
&lt;p&gt;OpenCVの5-point algorithmは以下の論文を実装してるはずなので、軽く読んでみたけどやっぱり焦点距離は必要ない気がしたので、OpenCVのコードを見てみました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.129.1518&amp;amp;rep=rep1&amp;amp;type=pdf&quot;&gt;Nistér, D. An efficient solution to the five-point relative pose problem, CVPR 2003.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;findEssentialMat&lt;/h2&gt;
&lt;p&gt;OpenCVで5-point algorithmを扱っているのはfindEssentialMatという関数で、中身はこんな感じになっています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Input should be a vector of n 2D points or a Nx2 matrix
cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, InputArray _cameraMatrix,
                              int method, double prob, double threshold, OutputArray _mask)
{
    CV_INSTRUMENT_REGION();

    Mat points1, points2, cameraMatrix;
    _points1.getMat().convertTo(points1, CV_64F);
    _points2.getMat().convertTo(points2, CV_64F);
    _cameraMatrix.getMat().convertTo(cameraMatrix, CV_64F);

    int npoints = points1.checkVector(2);
    CV_Assert( npoints &amp;gt;= 0 &amp;amp;&amp;amp; points2.checkVector(2) == npoints &amp;amp;&amp;amp;
                              points1.type() == points2.type());

    CV_Assert(cameraMatrix.rows == 3 &amp;amp;&amp;amp; cameraMatrix.cols == 3 &amp;amp;&amp;amp; cameraMatrix.channels() == 1);

    if (points1.channels() &amp;gt; 1)
    {
        points1 = points1.reshape(1, npoints);
        points2 = points2.reshape(1, npoints);
    }

    double fx = cameraMatrix.at&amp;lt;double&amp;gt;(0,0);
    double fy = cameraMatrix.at&amp;lt;double&amp;gt;(1,1);
    double cx = cameraMatrix.at&amp;lt;double&amp;gt;(0,2);
    double cy = cameraMatrix.at&amp;lt;double&amp;gt;(1,2);

    points1.col(0) = (points1.col(0) - cx) / fx;
    points2.col(0) = (points2.col(0) - cx) / fx;
    points1.col(1) = (points1.col(1) - cy) / fy;
    points2.col(1) = (points2.col(1) - cy) / fy;

    // Reshape data to fit opencv ransac function
    points1 = points1.reshape(2, npoints);
    points2 = points2.reshape(2, npoints);

    threshold /= (fx+fy)/2;

    Mat E;
    if( method == RANSAC )
        createRANSACPointSetRegistrator(makePtr&amp;lt;EMEstimatorCallback&amp;gt;(), 5, threshold, prob)-&amp;gt;run(points1, points2, E, _mask);
    else
        createLMeDSPointSetRegistrator(makePtr&amp;lt;EMEstimatorCallback&amp;gt;(), 5, prob)-&amp;gt;run(points1, points2, E, _mask);

    return E;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;29行目から32行目のこの部分で内部パラメータを使っています。どうにも画像座標をカメラ座標へ変換するのに使っているようです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;points1.col(0) = (points1.col(0) - cx) / fx;
points2.col(0) = (points2.col(0) - cx) / fx;
points1.col(1) = (points1.col(1) - cy) / fy;
points2.col(1) = (points2.col(1) - cy) / fy;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、38行目で焦点距離でRANSACで使う閾値を除算しています。これは29~32行目で焦点距離で除算してるのに合わせてるだけっぽいです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;threshold /= (fx+fy)/2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なのでおそらく、ここでは焦点距離の値は何でもよくて、画像座標系の原点を画像の左上から画像の中心(光学中心)に移動させるっていう意味合いが強いのだと思います。&lt;/p&gt;
&lt;p&gt;ですので、あらかじめ各特徴点を光学中心を原点にしたものに変換してから_points1, _points2に与えてやれば、内部パラメータは焦点距離=1.0, 光学中心=(0.0, 0.0)としても多分問題ないです。&lt;/p&gt;
&lt;p&gt;だからこんな感じでデフォルトパラメータの焦点距離が1.0で光学中心が(0.0, 0.0)になっているのでしょうね。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CV_EXPORTS_W Mat findEssentialMat( InputArray points1, InputArray points2,
                                 double focal = 1.0, Point2d pp = Point2d(0, 0),
                                 int method = RANSAC, double prob = 0.999,
                                 double threshold = 1.0, OutputArray mask = noArray() );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;findEssentialMatは2枚の画像で同じ内部パラメータを使う前提なのですが、あらかじめカメラ座標系に変換してやって_points1, _points2に渡してやることで、2枚の画像で別々の内部パラメータを使うことができるはずです。&lt;/p&gt;
&lt;p&gt;※ 論文もコードも深追いしたわけではないので、間違えがあれば教えてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- START MoshimoAffiliateEasyLink --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
(function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a;
b[a]=b[a]||function(){arguments.currentScript=c.currentScript
||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)};
c.getElementById(a)||(d=c.createElement(f),d.src=g,
d.id=a,e=c.getElementsByTagName(&quot;body&quot;)[0],e.appendChild(d))})
(window,document,&quot;script&quot;,&quot;//dn.msmstatic.com/site/cardlink/bundle.js?20220329&quot;,&quot;msmaflink&quot;);
msmaflink({&quot;n&quot;:&quot;詳解 OpenCV 3 ―コンピュータビジョンライブラリを使った画像処理・認識&quot;,&quot;b&quot;:&quot;オライリージャパン&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/51PnbcCkunL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/4873118379&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/4873118379&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/%E8%A9%B3%E8%A7%A3%20OpenCV%203%20%E2%80%95%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%93%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E7%94%BB%E5%83%8F%E5%87%A6%E7%90%86%E3%83%BB%E8%AA%8D%E8%AD%98/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=%E8%A9%B3%E8%A7%A3%20OpenCV%203%20%E2%80%95%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%93%E3%82%B8%E3%83%A7%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E7%94%BB%E5%83%8F%E5%87%A6%E7%90%86%E3%83%BB%E8%AA%8D%E8%AD%98&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;se79w&quot;,&quot;s&quot;:&quot;s&quot;});
&amp;lt;/script&amp;gt;
&amp;lt;div id=&quot;msmaflink-se79w&quot;&amp;gt;リンク&amp;lt;/div&amp;gt;
&amp;lt;!-- MoshimoAffiliateEasyLink END --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>SfMのOSS：Bundlerを動かしてみる in Windows 10 (動きませんでした)</title><link>https://mem-archive.com/posts/2018/09/09/post-328/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/09/09/post-328/</guid><pubDate>Sun, 09 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h4&gt;最初に結論を述べておくと、&lt;strong&gt;Windows 10でBundlerは動きませんでした&lt;/strong&gt;。ごめんなさい。&lt;/h4&gt;
&lt;p&gt;動かないまま力尽きたものの、結構頑張って動かそうとしたので、その軌跡をまとめました。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;SfMのOSSであるBundlerは、Linuxで動かすことを前提としています。&lt;/p&gt;
&lt;p&gt;なので、Windowsで動かすには昔はCygwin等が必要だったのですが、Windows 10のBash on Windows（BoW）を使えば簡単に動かせるんじゃないかと思ったので、挑戦してみます。&lt;br /&gt;
(一応Visual Studio用のプロジェクトファイルもあるのですが、VS上で動かすのは果てしなく手間がかかりそうなので、今回は見送ります)&lt;/p&gt;
&lt;p&gt;最新バージョンのv0.4でさえ2010年4月にリリースされたものなので今更感は否めませんが、大規模なSfMの礎となった偉大なOSSなので、動かしてみる価値はあるでしょう。&lt;/p&gt;
&lt;h3&gt;Bundlerの入手&lt;/h3&gt;
&lt;p&gt;何はともあれ、まずはBundlerのソースコードを持ってきます。 せっかくなので、公式サイトのv0.4ではなくGitHubから最新のコードを持ってきましょう。&lt;/p&gt;
&lt;p&gt;これからの処理は全部Bash on Windows上で行っています。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git clone https://github.com/snavely/bundler_sfm.git # Bundlerのソースコードを入手
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bundlerには特徴点検出器は含まれていないので、検出器のOSSもダウンロードします。 今回はREADMEにしたがって、以下のサイトのSIFT検出器を使います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.ubc.ca/~lowe/keypoints/&quot;&gt;Demo Software: SIFT Keypoint Detector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;$ wget http://www.cs.ubc.ca/~lowe/keypoints/siftDemoV4.zip # SIFT検出器をダウンロード
$ unzip siftDemoV4.zip # zipを展開
$ cp siftDemoV4/sift bundler_sfm/bin/ # 必要なファイルをBundlerにコピー
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみに、このSIFT検出器の出力フォーマットに合わせて特徴点を記述してやれば、SIFT検出器以外も使えるみたいです。&lt;/p&gt;
&lt;h3&gt;Bundlerのビルド&lt;/h3&gt;
&lt;p&gt;このままでは色々と足りなくてビルドできないので、必要なパッケージをインストールしてやります。&lt;/p&gt;
&lt;p&gt;JPEG画像のメタ情報、EXIFを扱うために、jheadというコマンドもインストールします。&lt;br /&gt;
Bundlerでは焦点距離をEXIFからとってきたりしているので、そのために使用するツールです。&lt;/p&gt;
&lt;p&gt;またプログラム内から画像の操作をするためにImageMagicもインストールします。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt install jhead # jheadのインストール
$ sudo apt install imagemagick # ImageMagicのインストール
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に、makeするときに必要な実行ファイルを見つけられるように、パスを通してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:Bundlerのパス/bin # 環境変数を設定
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでREADMEに書いてある準備は一通り行ったはずなのですが、実はこれではビルドに失敗してしてしまします。&lt;/p&gt;
&lt;p&gt;BoWはデフォルトでインストールされてるパッケージが少ないせいですかね。。。&lt;br /&gt;
なので、その他必要な諸々をインストールしてやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt install gfortran       # 実はFortranのコンパイラが必要みたいなのでインストール
$ sudo apt install zlib1g-dev     # 圧縮ファイルを扱うライブラリ
$ sudo apt install libjpeg-dev    # JPEG画像を扱うライブラリ
$ sudo apt install liblapacke-dev # 数値計算ライブラリ
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで準備が整ったので、ビルドしてやりましょう！&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ make # ビルド実行！
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Errorが出なければ、ビルド成功です！！(warningはたくさん出ますが無視してください。)&lt;/p&gt;
&lt;h3&gt;Bundlerの実行&lt;/h3&gt;
&lt;p&gt;ビルドが成功したら、Bundlerを実行してみましょう！！ 今回はリポジトリ内にあるサンプル画像を使って動かしてみます。&lt;/p&gt;
&lt;p&gt;まずは、画像のあるディレクトリに移動してやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir image
$ cp examples/ET/*.jpg image/ # 既存のファイル構造を汚したくないので、サンプル画像を新しいディレクトリにコピー
$ ls image/
et000.jpg  et001.jpg  et002.jpg  et003.jpg  et004.jpg  et005.jpg  et006.jpg  et007.jpg  et008.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとは、スクリプトを起動するだけ！！なはずなのですが・・・&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ../RunBundler.sh
Using default config options
[extract_focal] Error: jhead not found.  Please install jhead to ../bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;怒られました。。。&lt;/p&gt;
&lt;p&gt;jheadはさっきインストールしましたが、どうやらbin以下に置かないといけないみたいなので、シンボリックを貼ってやります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd .. # Bundler直下のディレクトリに移動
$ ln -s `which jhead` bin/ # binの下にシンボリックリンクを貼る
$ cd image # 画像のあるディレクトリに戻る
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;そして、再度実行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ../RunBundler.sh
Using default config options
0
Image list is list_tmp.txt
[Extracting exif tags from image ./et000.jpg]
  [Focal length = 5.400mm]
[Couldn&apos;t find CCD width for camera Canon Canon PowerShot A10]
[Found in EXIF tags]
  [CCD width = 5.230mm]
  [Resolution = 640 x 480]
  [Focal length (pixels) = 660.803

...

[- Extracting keypoints -]
../bin/sift: 1: ../bin/sift: Syntax error: &quot;(&quot; unexpected

...

[- Matching keypoints (this can take a while) -]
../bin/KeyMatchFull list_keys.txt matches.init.txt -1
Invalid keypoint file.

...

[KeyMatchFull] Reading keys took 0.016s
[- Running Bundler -]
bundler: BaseApp.h:271: std::vector&amp;lt;KeypointMatch&amp;gt;&amp;amp; MatchTable::GetMatchList(MatchIndex): Assertion `p.first != p.second&apos; failed.
../RunBundler.sh: line 148: 24563 Aborted                 (core dumped) $BUNDLER $IMAGE_LIST --options_file options.txt &amp;gt; bundle/bundle.log
[- Done -]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;うーむ、今度は画像は読み込めてるけど特徴点検出に失敗しまくってます。。。&lt;br /&gt;
はい、ここで挫折してBundlerを実行することはできませんでした。&lt;/p&gt;
&lt;h3&gt;結論&lt;/h3&gt;
&lt;p&gt;Syntax error: &quot;(&quot; unexpected というエラーメッセージ的に、どうにもSIFT検出器のバイナリが32bitしか対応していないっぽいです。&lt;br /&gt;
今回使用したこのSIFT検出器は、2005年が最終更新でソースコードもすべては公開されておらず自分の環境用にビルドできないため、64bit環境で動かないのはもうどうしようもありません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.ubc.ca/~lowe/keypoints/&quot;&gt;Demo Software: SIFT Keypoint Detector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;調べた感じBoWも64bitしかサポートしてないないようなので、違う特徴点検出器を使うしかないみたいですね。OpenCVとかを使えばできないことはないんでしょうけど、今回はここで力尽きたので見送りたいと思います。&lt;/p&gt;
</content:encoded></item><item><title>個人的おすすめVisual Studio拡張機能</title><link>https://mem-archive.com/posts/2018/09/09/post-589/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/09/09/post-589/</guid><pubDate>Sun, 09 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;WindowsでC/C++の開発をするときはVisual Studio 2017を使っているのですが、OS再インストールしたりPC新調するたびにどんな拡張機能入れてたか忘れて地味に困るので、記事に残しておくことにしました。&lt;/p&gt;
&lt;h3&gt;ログの出力に色をつける - VSColorOutput -&lt;/h3&gt;
&lt;p&gt;こんな感じでVisual Studioのログ出力に色をつけてくれます。Warningなら黄色、Errorなら赤色をつけてくれます。&lt;br /&gt;
ビルド中でも一目で問題があるかどうか判断できるため、大きなプログラムを開発するときには必須です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;公式：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=MikeWard-AnnArbor.VSColorOutput&quot;&gt;VSColorOutput - Visual Studio Marketplace&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Vimキーバインドを設定する - VsVim -&lt;/h3&gt;
&lt;p&gt;全Vimmerに必須のプラグインです。Visual StudioのキーバインドをVimに近づけてくれます。&lt;br /&gt;
ある程度自由にカスタマイズできるため、私のようなにわかVimmerには十分です。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A32.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;公式：&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=JaredParMSFT.VsVim&quot;&gt;VsVim - Visual Studio Marketplace&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>C言語のフレキシブル配列メンバ（flexible array member）、通称struct hack</title><link>https://mem-archive.com/posts/2018/08/10/post-529/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/08/10/post-529/</guid><pubDate>Fri, 10 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;PHPの内部実装がC言語なのは周知の事実かと思いますが、最近仕事でPHP7の内部実装をみる機会がありました。&lt;br /&gt;
そこで学んだC言語のflexible array member、通称struct hackというテクニックがなかなか面白かったので、忘れないようメモです。&lt;/p&gt;
&lt;p&gt;ちなみに、このサイトの説明がわかりやすいです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www44.atwiki.jp/bokuyo/pages/118.html&quot;&gt;structハック (struct hack) - Flexible array member - ぼく用あれこれまとめ - atwiki（アットウィキ）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;struct hack&lt;/h2&gt;
&lt;p&gt;struct hackはどんなテクニックかというと、こんな風に構造体の最後を要素数を定義しない配列にしてもコンパイル通りますよ！っていうテクニックです。&lt;/p&gt;
&lt;p&gt;別に構造体の中なら最後じゃなくてもコンパイルは通ると思うんですが、たぶん最後以外はうまい使い方はないです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Struct{
  char hoge;  
  char fuga[];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみに要素数を指定しない配列は構造体内でしか宣言できなくて、それ以外の場所で使おうとするとコンパイルエラーになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

int main(){   
  char piyo[];
  return 0;   
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;% g++ structHack.cc             
structHack.cc: In function ‘int main()’:
structHack.cc:10:13: error: storage size of ‘piyo’ isn’t known
   char piyo[];
             ^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみにサイズは0バイト、つまり要素数0の配列と認識されるようです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;                         
                                            
struct Struct{                              
  char hoge;                                
  char fuga[];                              
};                                          
                                            
int main(){                                 
  std::cout &amp;lt;&amp;lt; sizeof(Struct) &amp;lt;&amp;lt; std::endl; // 1と出力
  return 0;                                 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;struct hackの使いどころ&lt;/h2&gt;
&lt;p&gt;で、コンパイル通るのわかったけど、こんなん何に使うのよ？っていう話なんですが、こんな感じで使います。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;                                                  
#include &amp;lt;stdlib.h&amp;gt;                                                  
                                                                     
struct Struct{                                                       
  char hoge;                                                         
  char fuga[];                                                       
};                                                                   
                                                                     
int main(){                                                          
  Struct* piyo = (Struct*)malloc(sizeof(Struct) + sizeof(char)*10); // 無理やりメモリ領域を確保
  piyo-&amp;gt;fuga[8] = 100; // 要素数を定義してないのに安全にアクセスできる！！
  
  free(piyo);                                                                   
  return 0;                                                          
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この時のメモリの領域はこんな感じに確保されるので、メモリを解放するまで安全にアクセスできます。 しかも、malloc時にはじめてfugaの要素数を決められるので、構造体毎に異なる要素数を指定できます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;余談：PHP7でのstruct hackの使われ方&lt;/h2&gt;
&lt;p&gt;PHP7ではstruct hackはこんな感じで使われています。&lt;br /&gt;
properties_table[]ではなくproperties_table[1]と書いてるのは、おそらくC99より前のバージョンのC言語ではサイズ0の配列の宣言ができなかったらしいので、その辺の名残りでしょう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct _zend_object {
	zend_refcounted_h gc;
	uint32_t          handle; // TODO: may be removed ???
	zend_class_entry *ce;
	const zend_object_handlers *handlers;
	HashTable        *properties;
	zval              properties_table[1];　// &amp;lt;- struct hack!!
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;さらに余談ですが、PHP5とPHP7ではこのzend_objectの構造が変わっているため、PHP7ではzend_objectを使って構造体を定義するときは、こんな風にzend_objectを最後に持ってくるようにする必要があります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Struct2 {
   void  *hogehoge;
   zend_object std; // 絶対に最後じゃなきゃいけない。PHP5では最初に書くのが一般的だった(らしい)
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/hnw/items/d615e0d4122f247d4c75&quot;&gt;PHP5とPHP7の違い（extension編） #PHPExtension - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.buildinsider.net/language/clang/01&quot;&gt;C言語の最新事情を知る： C99の仕様 - Build Insider&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>JavaのListIteratorのnext()とprevious()が同じ値を指す話</title><link>https://mem-archive.com/posts/2018/05/12/post-494/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/05/12/post-494/</guid><pubDate>Sat, 12 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import MoshimoAffiliate from &quot;@components/misc/MoshimoAffiliate.astro&quot;
import img1 from &quot;./キャプチャ.jpg&quot;
import img2 from &quot;./キャプチャ2.jpg&quot;
import img3 from &quot;./キャプチャ3.jpg&quot;
import img4 from &quot;./キャプチャ4.jpg&quot;&lt;/p&gt;
&lt;p&gt;最近ListIteratorでハマったのでちょっと忘れないようメモです。&lt;/p&gt;
&lt;p&gt;ハマったのはこんなシチュエーションです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import java.util.LinkedList;
import java.util.ListIterator;

public class Main {
        public static void main(String[] args) {
            LinkedList&amp;lt;Integer&amp;gt; list = new LinkedList&amp;lt;Integer&amp;gt;();
            ListIterator&amp;lt;Integer&amp;gt; listItr;

            list.add(1);
            list.add(2);
            list.add(3);
            listItr = list.listIterator();

            System.out.println(listItr.next());         // 1が出力
            System.out.println(listItr.next());         // 2が出力
            System.out.println(listItr.previous());     // 1が出力されると思ったけど2が出力
            System.out.println(listItr.previous());     // 1が出力
            System.out.println(listItr.next());         // 2が出力されると思ったけど1が出力
            System.out.println(listItr.next());         // 2が出力
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;next()の次にprevious()、previous()の次にnext()を呼ぶと予想と反して同じ値を返してきます。&lt;br /&gt;
Javaの&lt;a href=&quot;https://docs.oracle.com/javase/jp/9/docs/api/java/util/ListIterator.html#next--&quot;&gt;公式ドキュメント&lt;/a&gt;を調べたらどうにもこれが正常な動作らしいので、なんでこうなるかもう少し調べてみました。&lt;/p&gt;
&lt;p&gt;ListIterator初期状態はこんな感じになっています。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ここで1回next()を呼ぶとイテレータが指してる右側の値の1が返ってきて、イテレータが1つ進みます。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;さらにもう1回next()を呼ぶと、イテレータの右側の2が返ってきて、イテレータが進んでこの状態になります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ここでprevious()を呼ぶと、イテレータの左側の2が返ってきて、イテレータは1つ戻ります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;イテレータを進める前の右側の値と、進めた後の左側の値は同じ状態&lt;/strong&gt;」、これがnext()とprevious()を交互に呼んだ時のからくりです。&lt;/p&gt;
&lt;p&gt;&amp;lt;MoshimoAffiliate data={{
&quot;n&quot;:&quot;Effective Java 第3版&quot;,&quot;b&quot;:&quot;&quot;,&quot;t&quot;:&quot;&quot;,&quot;d&quot;:&quot;https://m.media-amazon.com&quot;,&quot;c_p&quot;:&quot;&quot;,&quot;p&quot;:[&quot;/images/I/514iD6-p+dL.&lt;em&gt;SL500&lt;/em&gt;.jpg&quot;],&quot;u&quot;:{&quot;u&quot;:&quot;https://www.amazon.co.jp/dp/B099922DML&quot;,&quot;t&quot;:&quot;amazon&quot;,&quot;r_v&quot;:&quot;&quot;},&quot;v&quot;:&quot;2.1&quot;,&quot;b_l&quot;:[{&quot;id&quot;:1,&quot;u_tx&quot;:&quot;Amazonで見る&quot;,&quot;u_bc&quot;:&quot;#f79256&quot;,&quot;u_url&quot;:&quot;https://www.amazon.co.jp/dp/B099922DML&quot;,&quot;a_id&quot;:1183162,&quot;p_id&quot;:170,&quot;pl_id&quot;:27060,&quot;pc_id&quot;:185,&quot;s_n&quot;:&quot;amazon&quot;,&quot;u_so&quot;:1},{&quot;id&quot;:2,&quot;u_tx&quot;:&quot;楽天市場で見る&quot;,&quot;u_bc&quot;:&quot;#f76956&quot;,&quot;u_url&quot;:&quot;https://search.rakuten.co.jp/search/mall/Effective%20Java%20%E7%AC%AC3%E7%89%88/&quot;,&quot;a_id&quot;:1183160,&quot;p_id&quot;:54,&quot;pl_id&quot;:27059,&quot;pc_id&quot;:54,&quot;s_n&quot;:&quot;rakuten&quot;,&quot;u_so&quot;:2},{&quot;id&quot;:3,&quot;u_tx&quot;:&quot;Yahoo!ショッピングで見る&quot;,&quot;u_bc&quot;:&quot;#66a7ff&quot;,&quot;u_url&quot;:&quot;https://shopping.yahoo.co.jp/search?first=1\u0026p=Effective%20Java%20%E7%AC%AC3%E7%89%88&quot;,&quot;a_id&quot;:1187811,&quot;p_id&quot;:1225,&quot;pl_id&quot;:27061,&quot;pc_id&quot;:1925,&quot;s_n&quot;:&quot;yahoo&quot;,&quot;u_so&quot;:3}],&quot;eid&quot;:&quot;0Hz7j&quot;,&quot;s&quot;:&quot;s&quot;
}} /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その3</title><link>https://mem-archive.com/posts/2018/03/31/post-467/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/31/post-467/</guid><pubDate>Sat, 31 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;前回&lt;/a&gt;や&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;前々回&lt;/a&gt;は、2枚の画像ペアから特徴点の誤対応を除去する方法について説明しました。&lt;/p&gt;
&lt;p&gt;この記事では、2枚といわずすべての画像を使って誤対応を除去する方法について説明します。&lt;/p&gt;
&lt;h3&gt;特徴点追跡による誤対応の除去&lt;/h3&gt;
&lt;p&gt;まず、各画像間の各特徴点マッチング結果をつなぎ合わせ、複数枚の画像に渡って特徴点を追跡してやります。 ほとんどの特徴点の追跡結果はこんな感じになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A330.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;しかし、一部の特徴点結果はこんな感じ追跡結果が出てきます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A331.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;この場合、どれが正しい追跡結果かわかりません。なのでBundlerでは、この特徴点追跡に使われた特徴点マッチングはすべて除去してやります。&lt;/p&gt;
&lt;p&gt;余談ですが、この特徴点追跡の誤対応除去、理屈はわかるんですが上手い実装方法がわかりません。単純に実装したら結構な計算量になる気がします。&lt;/p&gt;
&lt;p&gt;ここまで3回に渡って、Bundlerの特徴点マッチングについて説明しました。 次回からは、最初の画像ペアの3次元復元について解説していきます。&lt;/p&gt;
&lt;p&gt;Bundlerについての解説記事一覧はこちら:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/06/post-309/&quot;&gt;Bundlerのアルゴリズムを追ってみる 概要編&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/31/post-467/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その2</title><link>https://mem-archive.com/posts/2018/03/24/post-441/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/24/post-441/</guid><pubDate>Sat, 24 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import img1 from &quot;./キャプチャ29.png&quot;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;前回&lt;/a&gt;は、Bundlerで使われている特徴点マッチングと、特徴点空間上の距離を使った誤対応の除去について説明しました。&lt;/p&gt;
&lt;p&gt;この記事では、特徴点空間上の距離を使った誤対応除去の次に行う、エピポーラ幾何を使った誤対応の除去について説明します。&lt;/p&gt;
&lt;h3&gt;エピポーラ幾何を使った誤対応の除去&lt;/h3&gt;
&lt;p&gt;説明といっても、ここではRANSACを使って基礎行列(Fundamental Matrix)を推定するという、一般的な手法を行っているだけです。&lt;/p&gt;
&lt;p&gt;すなわち、エピポーラ線から大きく外れる特徴点ペアは外れ値として削除してやろうよ、っていう考え方ですね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ちなみにBundlerでは、ランダムに８点選んで8-point algorithmで基礎行列を導出しています。&lt;/p&gt;
&lt;p&gt;基礎行列のエピポーラ拘束式といえばこれですね。&lt;/p&gt;
&lt;p&gt;$$
\boldsymbol{x}_2^T\boldsymbol{F}\boldsymbol{x}_1=0
$$&lt;/p&gt;
&lt;p&gt;間違った特徴点ペアは、0から大きく外れるため誤対応とみなすことができます。&lt;br /&gt;
問題は、正しい特徴点ペアもノイズの影響でぴったりと0にはならないため、閾値をどこに引くかです。&lt;/p&gt;
&lt;p&gt;Bundlerでは、画像の縦のサイズと横のサイズの大きな方に、0.006を掛けた値を閾値としています。&lt;br /&gt;
例えば、1024ピクセル×768ピクセルの画像だったら、1024×0.006=6.144ピクセルが閾値になります。&lt;/p&gt;
&lt;p&gt;この閾値よりもエピポーラ線から離れている特徴点ペアは、誤対応として除去してやります。&lt;/p&gt;
&lt;p&gt;次回は、特徴点追跡による誤対応の除去について説明します。&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Bundlerについての解説記事一覧はこちら:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/06/post-309/&quot;&gt;Bundlerのアルゴリズムを追ってみる 概要編&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/31/post-467/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その1</title><link>https://mem-archive.com/posts/2018/03/12/post-371/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/12/post-371/</guid><pubDate>Mon, 12 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import img1 from &quot;./キャプチャ23.png&quot;
import img2 from &quot;./キャプチャ24.png&quot;
import img3 from &quot;./キャプチャ25.png&quot;
import img4 from &quot;./キャプチャ26.png&quot;&lt;/p&gt;
&lt;p&gt;Structure from Motion(SfM)のオープンソース、Bundlerのアルゴリズムを追ってみます。&lt;/p&gt;
&lt;p&gt;今回は、特徴点マッチングと、特徴点空間上の距離を使った誤対応の除去について解説してみます。&lt;/p&gt;
&lt;h3&gt;特徴点マッチング&lt;/h3&gt;
&lt;p&gt;Bundlerでは、SIFT使って特徴点検出をしています。&lt;br /&gt;
で、各画像間で特徴点マッチングをするわけですが、後の処理のため各特徴点に対して最も距離の小さい(似ている)特徴点だけでなく、2番目に距離の小さい特徴点も計算してやります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;図&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ここでいう距離とは、特徴点空間上の距離のことです。&lt;br /&gt;
SIFTの特徴量は128次元ベクトルなので、この場合128次元空間の2点間の距離のことです。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;ただ、各画像数百とか数千の特徴点があるはずなので、各画像間ですべての特徴点に対して似ている特徴点を全探索してやると、とんでもない計算量になってしまいます。&lt;/p&gt;
&lt;p&gt;そこで、Bundlerでは最近傍探索(Nearest neighbor search: NNS)で全探索してやるのではなく、近似最近傍探索(Approximate nearest neighbor: ANN)を使って計算量を減らしています。&lt;/p&gt;
&lt;p&gt;ANNは最も距離が小さいと&lt;strong&gt;思われる&lt;/strong&gt;特徴点を高速に見つけ出す手法です。&lt;br /&gt;
NNSでは全探索で確実に最も距離の小さい特徴点を見つけますが、ANNではおそらく最も距離の小さいであろう特徴点を見つけます。&lt;br /&gt;
そのため、実は距離が最も小さくないこともありますが、その代わりにNNSに比べて非常に高速化されています。&lt;/p&gt;
&lt;p&gt;ちなみに、OpenCVでは&lt;code&gt;cv::FlannBasedMatcher&lt;/code&gt;という関数でANNを使った特徴点マッチングができます。&lt;/p&gt;
&lt;p&gt;余談ですが、Bundlerのソースコード読んだ感じ、特徴点のクロスチェックしていないっぽいです。。。&lt;br /&gt;
論文を読んだ感じ、Bundlerは特徴点の誤対応除去にめちゃくちゃ神経を使っているイメージだったので、ちょっと意外です。なにか理由があるんでしょうか？ (知っている人がいたら教えてください。)&lt;/p&gt;
&lt;h3&gt;特徴点空間上の距離を使った誤対応の除去&lt;/h3&gt;
&lt;p&gt;さて、ただ特徴点マッチングしただけでは、どうしても誤った特徴点の対応付けが残ってしまいます。&lt;br /&gt;
そこでBundlerでは、最も距離の小さい特徴点と2番目に距離の小さい特徴点を使って、誤対応を除去してやります。&lt;/p&gt;
&lt;p&gt;まずはこちらの図をご覧ください。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;この場合、最も似ている特徴点は明らかですね。&lt;/p&gt;
&lt;p&gt;ではこちらはどうでしょう？&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;図&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;どっちの方が距離が小さいか、ぱっと見わからなくないですか？&lt;br /&gt;
そりゃ、距離を計算してみれば明らかになるわけですが、極論距離が1.00000と1.00001だと、本当に1.00000の方が正しい対応点かどうか自信がなくなってしまいますよね。&lt;/p&gt;
&lt;p&gt;そこでBundlerでは、前者の図のように、最も似ている特徴点が明らかな点以外は削除してやります。&lt;br /&gt;
具体的には、この式を満たす特徴点マッチング以外は削除してやります。&lt;/p&gt;
&lt;p&gt;$$
\frac{最も似ている特徴点との距離}{２番目に似ている特徴点との距離}&amp;lt;0.6
$$&lt;/p&gt;
&lt;p&gt;0.6は閾値ですね。後者の図のような距離の差が少ないようなケースだと、左辺が1.0に近づくためその特徴点マッチングは削除されます。&lt;/p&gt;
&lt;p&gt; 
&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;次回&lt;/a&gt;は、エピポーラ幾何を使ってさらに誤対応を削除する方法について解説します。&lt;/p&gt;
&lt;p&gt; Bundlerについての解説記事一覧はこちら:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/06/post-309/&quot;&gt;Bundlerのアルゴリズムを追ってみる 概要編&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/31/post-467/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Bash on Windows + cmderで快適なCUI環境の構築</title><link>https://mem-archive.com/posts/2018/03/10/post-376/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/10/post-376/</guid><pubDate>Sat, 10 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Windows 10でBash on Windows（BoW）が導入されてから、なんだかんだでターミナル上で開発することが増えてきました。&lt;br /&gt;
なので、自分流快適なCUIの開発環境構築をメモします。&lt;/p&gt;
&lt;p&gt;今回は、cmderというWindows用のターミナルソフトを導入します。&lt;/p&gt;
&lt;h4&gt;cmderのインストール&lt;/h4&gt;
&lt;p&gt;まずはcmderをダウンロードします。cmderはWindows用のターミナルソフトです。 デフォルトのBoWターミナルはタブ機能とかなくて不便ですからね！&lt;/p&gt;
&lt;p&gt;ダウンロードはこちらから&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://cmder.net/&quot;&gt;cmder | Console Emulator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MiniとFullの2種類ありますが、変なトラブルに巻き込まれたくないので今回はFullをダウンロードします。 cmderはzip展開するだけで使えるので展開して好きなディレクトリに保存しましょう。&lt;/p&gt;
&lt;p&gt;で、起動してみるとこんな感じのターミナルが開きます。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A319.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;起動時にBash (Zsh)が起動するように設定&lt;/h4&gt;
&lt;p&gt;デフォルトでBashが起動するように設定しましょう。&lt;/p&gt;
&lt;p&gt;メニュー → Setting → Startup → Tasks → +ボタンでBashを起動する設定を追加してやります。&lt;br /&gt;
(ちなみに、Task parametersにはこの設定を起動したときのタスクバーのアイコンを設定するらしいです。私はここに間違ってBashのパスを書いてしまって、cmderに怒られまくりましたw)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A320.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;私はBashよりもZshの方が個のみなので、以下の設定でデフォルトでZshが起動するようにしました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\Windows\System32\bash.exe ~ -c &quot;export SHELL=`which zsh` &amp;amp;&amp;amp; exec `which zsh` -l&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bash使いの人はこっちを設定しましょう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\Windows\System32\bash.exe ~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最後に、新しく作った設定がデフォルトで起動するようにすると、cmder起動時にZshもしくはBashが起動するようになります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A321.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;ターミナルの透過率を設定&lt;/h4&gt;
&lt;p&gt;メニュー → Setting → Features → Transparencyで背景の透過率を設定してやります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A322.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;cmderの設定はこの辺のサイトを参考にさせていただきました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/euxn23/items/fb77435296b131fd0a3a&quot;&gt;Bash on Windows + cmderでまともなターミナルを獲得する(tmuxに関する追記有り)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.crz33.com/software/env/cmder&quot;&gt;Bash on Windows のターミナルをcmderにする&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://qiita.com/moh6an/items/e9c62d1ed717b63ad76c&quot;&gt;古の習わしに従いCmderコンソールを全画面半透過で運用する&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Bundlerのアルゴリズムを追ってみる 概要編</title><link>https://mem-archive.com/posts/2018/03/06/post-309/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/06/post-309/</guid><pubDate>Tue, 06 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Structure from Motionで多分一番有名なOSSである、Bundlerの処理を追ってみます。&lt;br /&gt;
何回かに記事を分けて解説していくつもりなので、途中で力尽きたらごめんなさい。。。&lt;/p&gt;
&lt;p&gt;Bundlerの公式サイトはこちらです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.cornell.edu/~snavely/bundler/&quot;&gt;Bundler: Structure from Motion (SfM) for Unordered Image Collections&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最新版はv0.4になっていますが、Githubのコミット履歴を見た感じ、今でも細々と有志による更新が行われているみたいですね。 v1.0が出ることは永遠になさそう・・・&lt;/p&gt;
&lt;p&gt; 
公式サイトによるとBundlerの基になった論文は２本あるみたいです。&lt;br /&gt;
SIGGRAPH 2006とIJCV2007の論文なので、おそらくSIGGRAPH で発表した内容をIJCVで詳細にジャーナル論文にまとめた感じだと思います。&lt;br /&gt;
IJCVの論文はこちらです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://phototour.cs.washington.edu/ModelingTheWorld_ijcv07.pdf&quot;&gt;Snavely, Noah, Steven M. Seitz, and Richard Szeliski. &quot;Modeling the world from internet photo collections.&quot; International journal of computer vision 80.2 (2008): 189-210.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この論文をベースに、論文に乗っていない細かなところはコードを見ながら、Bundlerの流れを追っていきたいと思います。&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;Bundlerの大まかな流れ&lt;/h2&gt;
&lt;p&gt;Bundlerの大まかな流れは一般的なSfMと変わりません。というか、Bundlerの論文に技術的な新規性はほとんどありません。&lt;/p&gt;
&lt;p&gt;この論文のすごいところは既存の技術を組み合わせて、時系列順でもない大規模な画像に対してSfMを成功させたところだと僕は思っています。&lt;/p&gt;
&lt;h4&gt;１．各画像間の特徴点マッチング&lt;/h4&gt;
&lt;p&gt;まずは何はともあれ各画像間で特徴点をします。BundlerではSIFTを使って対応点を求めています。&lt;br /&gt;
ただ、如何に精度の良い特徴量でも、単純な特徴点マッチングではどうしても誤対応が含まれてしまいます。&lt;/p&gt;
&lt;p&gt;Bundlerでは、この誤対応を&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;特徴点空間上の距離を使った除去&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;エピポーラ幾何を使った除去&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特徴点追跡による除去&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;の3つの方法を使って排除しています。&lt;/p&gt;
&lt;h4&gt;２．１組の画像間で３次元復元&lt;/h4&gt;
&lt;p&gt;Bundlerは初めに２枚の画像で３次元復元を行い、徐々に画像を追加して復元結果を大きくしていく、といった手法をとっています。&lt;/p&gt;
&lt;p&gt;この手の手法の最終的な復元精度は、最初の２枚の復元精度によって決まるといっても過言ではないので、最初の２枚の選び方がものすごく重要です。これがすべてといってもいいほどです。&lt;/p&gt;
&lt;p&gt;Bundlerでは最初の２画像の理想的な関係として、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;２画像間の対応点が多い&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;２枚の画像の撮影位置が離れている&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;の２つを挙げ、モホグラフィ変換を利用してこの２つの条件を満たす２画像を探す方法を説明しています。&lt;/p&gt;
&lt;p&gt;その後、5-point algorithmとTriangulationとかを使って、選ばれた最初の２画像の３次元復元をしています。&lt;/p&gt;
&lt;h4&gt;３．画像を１枚追加して３次元復元&lt;/h4&gt;
&lt;p&gt;最初の２画像で３次元復元ができたら、新しく画像を１枚ずつ追加して、復元結果を大きくしていきます。&lt;/p&gt;
&lt;p&gt;順番としては、&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;３次元座標が既知の特徴点が多い画像&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;から追加していきます。&lt;/p&gt;
&lt;p&gt;追加する画像は特徴点とその３次元座標がわかっているので、DLT法を使って内部パラメータ、外部パラメータを求めているみたいです。&lt;br /&gt;
そして、Triangulationでまだ３次元座標が求まっていなかった特徴点の３次元座標を計算しています。&lt;/p&gt;
&lt;p&gt;最後に、バンドル調整で各画像のパラメータと３次元点を最適化してやってます。&lt;/p&gt;
&lt;p&gt;これを、追加する画像がなくなるまで続けてるみたいです。&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;以上が、Bundlerのおおよその流れになります。 記事にしてみると意外と実装できちゃいそうな気がしますが、実際は精度を高めるためのテクニックが至る所に使われてるっぽくて、なかなか骨が折れそうです。&lt;/p&gt;
&lt;p&gt;次回から頑張って詳細な流れを解読していきたいと思います。&lt;/p&gt;
&lt;p&gt; Bundlerについての解説記事一覧はこちら:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/06/post-309/&quot;&gt;Bundlerのアルゴリズムを追ってみる 概要編&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/12/post-371/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/24/post-441/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2018/03/31/post-467/&quot;&gt;Bundlerのアルゴリズムを追ってみる 特徴点マッチング編 その3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>バンドル調整とは</title><link>https://mem-archive.com/posts/2018/03/04/post-259/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/04/post-259/</guid><pubDate>Sun, 04 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./キャプチャ6.png&quot;
import img2 from &quot;./キャプチャ7.png&quot;
import img3 from &quot;./キャプチャ13-1.png&quot;
import img4 from &quot;./キャプチャ8.png&quot;&lt;/p&gt;
&lt;p&gt;SfMとかSLAMの3D系の研究を始めると、必ず出てくるのがバンドル調整(Bundle Adjustment)です。&lt;br /&gt;
っちゃけただの非線形の最小二乗法なんですけど、今回はこのバンドル調整について解説します。&lt;/p&gt;
&lt;p&gt;バンドル調整、またはバンドル調整法とはずばり 「&lt;strong&gt;内部パラメータ、外部パラメータ、世界座標点群を最適化する非線形最小二乗法&lt;/strong&gt;」 です。&lt;/p&gt;
&lt;p&gt;まぁ、この説明で理解できる人はそもそもこの記事なんか見ないと思いますので、もう少し説明してみます。&lt;/p&gt;
&lt;h2&gt;バンドル調整の考え方&lt;/h2&gt;
&lt;p&gt;まず、SIFTとかSURFとかを使って、画像から検出した特徴点があるとします。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;そして、内部パラメータと外部パラメータ、抽出した特徴点の世界座標点が推定できているとします。&lt;/p&gt;
&lt;p&gt;この推定したパラメータを使って、世界座標点を画像座標点へ透視投影変換をしてみましょう。&lt;/p&gt;
&lt;p&gt;この変換した画像座標点、理想的には画像から抽出した特徴点とピッタリ合わないといけないんですが、様々なノイズの影響でそう上手くはいかないんですよね。。。&lt;/p&gt;
&lt;p&gt;ちなみに、この２つの点の距離のことを「&lt;strong&gt;再投影誤差&lt;/strong&gt;」といいます。&lt;br /&gt;
(もしピッタリ合っちゃったら、バンドル調整は不必要になりますw)&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;すなわち！ピッタリと合うようなパラメータ、世界座標点を見つけることができれば、それが正しい推定結果だろう、という考え方がバンドル調整です。&lt;br /&gt;
バンドル調整では、内部、外部パラメータと世界座標点をちょっとずつちょっとずつ微調整してやって、ピッタリと合うように調整してやります。&lt;/p&gt;
&lt;p&gt;数式で表すとこうなりますね&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;画像&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;$\boldsymbol{A}$は内部パラメータ、$\boldsymbol{R}$は回転行列、$\boldsymbol{t}$は並進ベクトル、そして$\boldsymbol{X_w}$(ベクトル)は世界座標点です。こいつらを微調整していくわけですね。&lt;/p&gt;
&lt;p&gt;なぜこの式になるかわからない人は、こちらの記事を一度読んでみてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/03/04/post-280/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;実際には特徴点は複数あって、ついでに画像も複数枚あるのが普通なので、すべてをピッタリ合わせるのは現実的には不可能です。&lt;/p&gt;
&lt;p&gt;そこで、特徴点と透視投影変換した画像座標点の距離の和が、全体で最小になるように内部、外部パラメータと世界座標点の座標を調整します。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;数式で表すとこうなります。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A314.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;$n$が画像の枚数、$m$は世界座標点数です。&lt;br /&gt;
$I_{ij}$は$i$番目の世界座標点が$j$番目の画像に写っていれば1、写っていなければ0になる関数です。全部の世界座標点が全部の画像に移るわけじゃないですからね！&lt;/p&gt;
&lt;p&gt;この式を頑張って偏微分して、レーベンマーク・マーカート法とか使って最小化してやれば、再投影誤差の和が一番小さい、一番それっぽい内部、外部パラメータと世界座標点を得ることができちゃうわけです。&lt;br /&gt;
(この記事で紹介した式は理解しやすさを第一に置いているので、この式が実際の研究で使われることはほぼないです。なのでこの式を頑張って偏微分しちゃわないようにしましょうw)&lt;/p&gt;
&lt;h2&gt;バンドル調整は内部パラメータも最適化すべきなのか&lt;/h2&gt;
&lt;p&gt;バンドル調整において、外部パラメータと世界座標点群は必ずと言っていいほど最適化するのですが、内部パラメータは最適化したりしなかったりします。&lt;/p&gt;
&lt;p&gt;SLAMとかだとあらかじめ内部パラメータをキャリブレーションしておくことがほとんどなので、内部パラメータは最適化するパラメータには含めません。&lt;br /&gt;
bundlerやVisualSfMのようなネットから集めた画像を対象にしたSfMだと含めることが多いです。&lt;/p&gt;
&lt;p&gt;最適化するパラメータは多いほど意図しない局所解に落ちやすくなるので、それなりの精度で内部パラメータがわかっているならば、含めない方が個人的には無難だと思います。&lt;br /&gt;
(昔私が実装したSfMでは、見事に局所解にハマって一部の実験データで焦点距離がマイナスになったことがありますw)&lt;/p&gt;
&lt;h2&gt;より詳しく知りたいなら&lt;/h2&gt;
&lt;p&gt;より深くバンドル調整を理解したい人は、こちらの論文がおすすめです。内容は少々難しめな気がしますが、日本語でバンドル調整を解説した論文としては一番しっかりしていると思います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://iim.cs.tut.ac.jp/member/kanatani/papers/budjust.pdf&quot;&gt;岩元祐輝, 菅谷保之, 金谷健一. &quot;3 次元復元のためのバンドル調整の実装と評価.&quot; コンピュータビジョンとイメージメディア (CVIM) 2011.19 (2011): 1-8.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;早くバンドル調整を使いたいなら&lt;/h2&gt;
&lt;p&gt;いやいや、この記事に書いてることは理解できたから、とにかく使いたい！！っていうせっかちな人は、以下のライブラリがおすすめです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://ceres-solver.org/index.html&quot;&gt;Ceres Solver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;非線形最小二乗問題を扱うときに非常に面倒な評価関数の微分を内部で自動でやってくれるので、実質評価関数を突っ込むだけで最適化問題を解いてくれます。&lt;/p&gt;
&lt;p&gt;バンドル調整のサンプルコードもあるので、お手軽に試せます。かなり複雑な評価関数でも扱えてしまうので、バンドル調整に限らずいろんな場面で役に立ってくれるはず！&lt;/p&gt;
&lt;p&gt;WindowsでのこのCeres Solverのビルド方法を記事にしてまとめてあるので、よければ参考にしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/09/30/post-633/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;Ceres Solverの注意事項としては、ライブラリ内部でやってるのはあくまで数値微分ということです。なので、どうしても本来の微分結果とは誤差があると思います。&lt;/p&gt;
&lt;p&gt;ですので、ライブラリに頼らず手動で頑張って微分した方が、もしかすると精度は上がるかもしれません。&lt;br /&gt;
(個人的には十分な精度のライブラリだと思いますが、0.01%でも精度を追い求めたい人は頭の隅に置いておきましょう)&lt;/p&gt;
</content:encoded></item><item><title>透視投影変換を展開してみる</title><link>https://mem-archive.com/posts/2018/03/04/post-280/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/03/04/post-280/</guid><pubDate>Sun, 04 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./キャプチャ4.png&quot;
import img2 from &quot;./キャプチャ9.png&quot;
import img3 from &quot;./キャプチャ10.png&quot;
import img4 from &quot;./キャプチャ11.png&quot;
import img5 from &quot;./キャプチャ12.png&quot;&lt;/p&gt;
&lt;p&gt;以前、透視投影変換について解説しました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/26/post-30/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;変換式はこんなのでしたよね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;で、この式の$s$って邪魔じゃないですか？ こいつがあるせいで、上の式の右辺を計算した後、最後に$s$で割るっていう、２段階手順が必要になっちゃうんですよね。&lt;/p&gt;
&lt;p&gt;そこで、なんの役に立つかはわかりませんが、画像座標を一発で出す式を導出してみます。&lt;/p&gt;
&lt;p&gt;まずは、世界座標点をカメラ座標点へ変換します。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;次は画像座標に変換しましょう。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;で、両辺を$s(=Z_c)$で割ってやれば、一発で画像座標に変換できる式の完成です。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;画像&quot;
style=&quot;width: 400px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;これを$[X_c,Y_c,Z_c]^t$を使わずに表すと、こんな感じになります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img5.src}
alt=&quot;画像&quot;
style=&quot;width: 700px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;結構ごちゃごちゃした式になっちゃいましたねw&lt;/p&gt;
&lt;p&gt;この式が何の役に立つかは正直思いつかないんですが、過去に何回か導出の必要に迫られたので、メモとして残しておきます。 なぜ導出の必要があったかは思い出せないw&lt;/p&gt;
</content:encoded></item><item><title>透視投影変換とは</title><link>https://mem-archive.com/posts/2018/02/26/post-30/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/02/26/post-30/</guid><pubDate>Mon, 26 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./キャプチャ4.png&quot;&lt;/p&gt;
&lt;p&gt;3次元系の画像処理の勉強では、透視投影カメラモデルの理解は必須です。&lt;/p&gt;
&lt;p&gt;そんなわけで今回は透視投影カメラモデルについて解説してみたいと思います。&lt;br /&gt;
(といっても、過去の記事でほぼ解説してしまった気がするので、この記事は簡単にまとめたものなります。)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://opencv.jp/opencv-2.1/cpp/camera_calibration_and_3d_reconstruction.html&quot;&gt;OpenCVでも紹介されている&lt;/a&gt;、言わずと知れた透視投影の式といえばこれですよね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;各行列を文字に置き換えるとこうなりますね。&lt;br /&gt;
$$
s\boldsymbol{x}=\boldsymbol{A[R|t]X}_w
$$&lt;/p&gt;
&lt;p&gt;さて、そもそも内部パラメータと外部パラメータとはなんぞや？についてですが、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;外部パラメータ：世界座標系からカメラ座標系への変換&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内部パラメータ：カメラ座標系から画像座標系への変換&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;を表しています。 つまり透視投影変換は世界座標→カメラ座標→画像座標という変換を表していることになります。&lt;/p&gt;
&lt;p&gt;それぞれ詳しく解説していきます。&lt;/p&gt;
&lt;h2&gt;外部パラメータ　～世界座標からカメラ座標への変換～&lt;/h2&gt;
&lt;p&gt;外部パラメータの持つ意味は、こちらの記事で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/17/post-74/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;内部パラメータ　～カメラ座標から画像座標への変換～&lt;/h2&gt;
&lt;p&gt;内部パラメータの持つ意味は、こちらの記事で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/21/post-157/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;ほぼ過去の記事を紹介するだけで終わってしまいましたw&lt;/p&gt;
&lt;p&gt;ぶっちゃけ、式さえわかっていれば透視投影変換はできるんですけど、内部パラメータと外部パラメータの幾何学的な意味を理解しているのとしていないのは応用力が段違いなので、完璧に身につけましょう。&lt;/p&gt;
</content:encoded></item><item><title>内部パラメータ(焦点距離)の単位の話 ～pixelとmmの変換～</title><link>https://mem-archive.com/posts/2018/02/25/post-201/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/02/25/post-201/</guid><pubDate>Sat, 24 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./キャプチャ.png&quot;
import img2 from &quot;./キャプチャ2.png&quot;
import img3 from &quot;./キャプチャ3.png&quot;&lt;/p&gt;
&lt;p&gt;さて、今回は内部パラメータの単位について解説してみたいと思います。&lt;/p&gt;
&lt;p&gt;皆さんご存知の通り、透視投影変換は&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;世界座標&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;↓&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;カメラ座標&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;↓&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;画像座標&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;の順で、世界座標を画像座標に変換していて、現実世界の3次元物体が画像上のどこに投影されるのかを示したものでした。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/26/post-30/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;ただ、ちょっと考えてみてください。&lt;br /&gt;
世界座標系やカメラ座標系、すなわち現実世界では物体の大きさはメートルとかセンチメートルとかミリメートルで表しますよね。&lt;/p&gt;
&lt;p&gt;一方で、画像座標系では投影された物体の大きさはピクセル単位。。。&lt;br /&gt;
あれ、いつの間にか単位が変わっちゃってますね！びっくりです。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 350px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;どこで変わっちゃったんでしょう。&lt;br /&gt;
ずばり、カメラ座標から画像座標に変換されるときです。&lt;/p&gt;
&lt;p&gt;内部パラメータに秘密があるんです。内部パラメータは覚えていますか？&lt;br /&gt;
覚えていない人はまずはこちらで思い出しましょう。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/21/post-157/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;内部パラメータ行列はこんな形をしていましたね。&lt;br /&gt;
$(f_{x}, f_{y})$は焦点距離、$(c_{x}, c_{y})$が光学中心です。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 250px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;で、実はこの焦点距離と光学中心、全部ピクセル単位なんですよ！&lt;br /&gt;
焦点距離の単位は一般的には$mm$なんですが、透視投影変換の式の焦点距離はピクセルなのです。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ちょっと待て、$mm$とピクセルってどうやって変換するんだよ&lt;/strong&gt;&lt;br /&gt;
とか&lt;br /&gt;
&lt;strong&gt;ピクセル単位なのは分かったけど、なんで$f_{x}$と$f_{y}$の2種類も焦点距離があるんだよ&lt;/strong&gt;&lt;br /&gt;
っていう声が聞こえてきそうなので、順に説明していきます。&lt;/p&gt;
&lt;p&gt;まずは、イメージセンサについて説明します。&lt;/p&gt;
&lt;p&gt;そもそも、カメラはイメージセンサというセンサに光を通すことで、現実の風景を画像に変換しています。&lt;br /&gt;
で、このセンサは画素数分の受光素子(フォトダイオード)を設置したセンサなんです。&lt;/p&gt;
&lt;p&gt;例えば、下のような14×8ピクセルの画像を撮影できるカメラには、縦14個、横8個の受光素子が設置されています。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A33.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;つまり、
$$
1ピクセルのmm単位の大きさ=\frac{イメージセンサの１辺の長さ}{１辺の受光素子数}
$$
が成り立つんです。&lt;/p&gt;
&lt;p&gt;これがわかれば、焦点距離の$mm$⇔ピクセルの変換は余裕ですね！&lt;/p&gt;
&lt;p&gt;さて、理想的には
$$
\frac{イメージセンサの縦の長さ}{縦の受光素子数}=\frac{イメージセンサの横の長さ}{横の受光素子数}
$$
が成り立つはずなんですが、悲しいかな人間の作った工業製品、微妙に成り立たないんだなこれが。。。&lt;/p&gt;
&lt;p&gt;しょうがないから横軸$x$軸の焦点距離と縦軸$y$軸の焦点距離を別々に考えようってなったのが、$f_x$と$f_y$です。すなわち、
$$
f_x=\frac{横の受光素子数}{イメージセンサの横の長さ}\times mm単位の焦点距離
$$
$$
f_y=\frac{縦の受光素子数}{イメージセンサの縦の長さ}\times mm単位の焦点距離
$$
ということですね。&lt;/p&gt;
&lt;p&gt;まあ、ぶっちゃけ最近のイメージセンサは精度がいいので、$f_x=f_y$と考えてもほとんど支障ありません。(精度を求める研究をしている人はちゃんと気にしましょう)&lt;/p&gt;
&lt;p&gt;余談ですが、バンドル調整とかで内部パラメータを非線形最適化するときは、$f_x=f_y$として扱った方が未知数が減ってむしろ精度が安定する気がします。(検証はしていません)&lt;/p&gt;
&lt;p&gt;技術書でもこの辺の話題は触れられなかったり軽く流されたりで、初学者の学生さんなんかは勘違いしている人結構いるんじゃないでしょうか？&lt;br /&gt;
(私は学生時代勘違いしていましたw)&lt;/p&gt;
&lt;p&gt;この記事では主にmmとpixel単位の変換の考え方について書きましたが、実際にJPEGのメタデータであるExifから、ピクセル単位の焦点距離を導出する方法を以下の記事にまとめました。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2019/07/28/post-1942/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>カメラ内部パラメータとは</title><link>https://mem-archive.com/posts/2018/02/21/post-157/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/02/21/post-157/</guid><pubDate>Wed, 21 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./スライド1.jpg&quot;
import img2 from &quot;./スライド2-2.jpg&quot;
import img3 from &quot;./スライド3-4.jpg&quot;
import img4 from &quot;./スライド4-1.jpg&quot;
import img5 from &quot;./スライド5-3.jpg&quot;
import img6 from &quot;./スライド2-3.jpg&quot;&lt;/p&gt;
&lt;p&gt;前回は外部パラメータの意味を解説しました。&lt;br /&gt;
なので今回は、外部パラメータと双璧をなす？内部パラメータの意味を解説してみたいと思います。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/17/post-74/&quot; /&amp;gt;&lt;/p&gt;
&lt;h2&gt;内部パラメータとは&lt;/h2&gt;
&lt;p&gt;内部パラメータとは、一言でいうと&lt;br /&gt;
「&lt;strong&gt;カメラ座標を画像座標へ変換する行列&lt;/strong&gt;」のことです。&lt;/p&gt;
&lt;p&gt;式で表すとこうなります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;例によって詳しく解説していきます。&lt;/p&gt;
&lt;h2&gt;内部パラメータの意味&lt;/h2&gt;
&lt;p&gt;ちょっと説明を簡単にするために、$f=f_x=f_y$と仮定して話を進めます。&lt;br /&gt;
なぜこのように仮定していいかは、こっちの記事を参考にしてください。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/02/25/post-201/&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;下の図のように、カメラ座標系上に複数の3次元点があるとします。&lt;br /&gt;
この点たちを、どうにかして青で示した画像平面に持って行ってやりたいわけです。&lt;/p&gt;
&lt;p&gt;ちなみに、画像平面はカメラ座標系原点から$Z_c$方向に$f$離れた位置にあります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;いきなり画像平面に持っていくのは少し難しいので、まずは各カメラ座標点を正規化してやって、$Z_{c}=1$の平面に持ってきてやりましょう。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;あとは簡単ですね！正規化したカメラ座標点たちを焦点距離$f$倍すれば画像平面に点を集められます！&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;この画像平面上に集めた3次元点の$x_c$軸と$y_c$軸の値が、画像座標になるわけです。&lt;/p&gt;
&lt;p&gt;ただ、このままだと画像平面と$z_c$軸の交点、画像の中央が画像座標系の原点になってしまいます。&lt;br /&gt;
別に問題ないといえばないんですが、普通は画像の左上を原点にしますよね？&lt;br /&gt;
だからこのままじゃちょっと扱いにくい。。。&lt;/p&gt;
&lt;p&gt;そこで、最後に左上が画像座標$(0, 0)$になるように、原点をずらしてあげましょう。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img5.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;これで一般的な画像の座標系上に、カメラ座標点を投影することができました！&lt;/p&gt;
&lt;p&gt;この流れを数式にまとめるとこうなります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img6.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;あれ？なんか最初に紹介した式と違いますね…&lt;br /&gt;
安心してください。式を展開すればわかりますが、実は$s=Z_c$なので、両方の式を展開すると同じ形になります。&lt;/p&gt;
&lt;p&gt;きっと最初の方の式の形にした方が、透視投影変換で世界座標 → カメラ座標 → 画像座標の変換を1つの式で、しかも掛け算だけで表すことができるのでいろいろと楽なのでしょう。&lt;/p&gt;
&lt;p&gt;これで&lt;strong&gt;内部パラメータの持つ意味はばっちり&lt;/strong&gt;ですね！！&lt;/p&gt;
&lt;p&gt;ちなみに反対に、&lt;strong&gt;内部パラメータを使った画像座標系からカメラ座標系の情報の取り出し方&lt;/strong&gt;については、こちらの記事で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/10/13/post-682/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>カメラ外部パラメータとは</title><link>https://mem-archive.com/posts/2018/02/17/post-74/</link><guid isPermaLink="true">https://mem-archive.com/posts/2018/02/17/post-74/</guid><pubDate>Sat, 17 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import LinkCard from &quot;@components/misc/LinkCard.astro&quot;
import img1 from &quot;./CV-1.jpg&quot;
import img2 from &quot;./スライド4.jpg&quot;
import img3 from &quot;./スライド5-1.jpg&quot;
import img4 from &quot;./スライド6-1.jpg&quot;
import img5 from &quot;./スライド9.jpg&quot;
import img6 from &quot;./スライド10.jpg&quot;
import img7 from &quot;./スライド5-2.jpg&quot;
import img8 from &quot;./スライド3.jpg&quot;&lt;/p&gt;
&lt;p&gt;外部パラメータとはなんぞや？という話ですが、一言でいうと&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;世界座標をカメラ座標に変換する行列&lt;/strong&gt;」のことです。&lt;/p&gt;
&lt;p&gt;式で表すとこうですね。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img1.src}
alt=&quot;画像&quot;
style=&quot;width: 600px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;待て待て、それだけ言われてもわからない。&lt;br /&gt;
という人の為に、もう少し詳しく解説してみたいと思います。&lt;/p&gt;
&lt;h2&gt;外部パラメータの意味&lt;/h2&gt;
&lt;p&gt;世界座標系は、下の図のようにすべての基準となる1つしかない座標系で、「&lt;strong&gt;すべての3次元点の絶対的な位置&lt;/strong&gt;」を表しています。&lt;br /&gt;
現実世界の緯度経度と高度と思ってもらえれば大丈夫だと思います。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img2.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;カメラ座標系は下の図のような座標系で「&lt;strong&gt;このカメラから見て3次元点がどの位置にあるか&lt;/strong&gt;」を表しています。&lt;br /&gt;
したがって画像の枚数だけカメラ座標系も存在します。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img3.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;さて、ではどうやって世界座標系で表現された点をカメラ座標系に変換しましょうか？&lt;br /&gt;
実はこれ、カメラの姿勢(回転行列)と位置(並進ベクトル)がわかれば、変換できちゃうんです。&lt;/p&gt;
&lt;p&gt;まず、世界座標点に回転行列を掛けて回転させてやります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img4.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;で、実は世界座標点を回転させるのって、世界座標系自体を逆回転させるのと、便宜的には同じなんです。&lt;/p&gt;
&lt;p&gt;さらに、世界座標系を回転行列分逆回転させると、なんとカメラ座標系と同じ方向を向くんです！← 重要&lt;/p&gt;
&lt;p&gt;つまり、世界座標点に回転行列を掛けることによって、【&lt;strong&gt;カメラ座標系の姿勢から見た、3次元点の見える方向&lt;/strong&gt;】を求めることができるのです！！&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img5.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img6.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;しかし、同じ方向を向いたとはいえ、世界座標系の原点とカメラ座標系の原点は違うので、まだカメラ座標にはなってません。なので、並進ベクトルを使って原点をそろえてあげましょう。&lt;/p&gt;
&lt;p&gt;さあ、これで世界座標系で見た3次元点をカメラ座標に変換することができました。&lt;br /&gt;
これを数式にまとめると以下のようになります。&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img7.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;これで外部パラメータの持つ意味は完璧ですね！&lt;/p&gt;
&lt;p&gt;最後に、同次座標というテクニックを使って式を変形してやれば、記事のはじめの式と同じ形になります。&lt;br /&gt;
(同次座標がわからない人も、両方の式を展開してみると同じ式であることが確認できるかと思います。)&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;text-align: center&quot;&amp;gt;
&amp;lt;img
src={img8.src}
alt=&quot;画像&quot;
style=&quot;width: 500px;  margin: 0 auto;&quot;
/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;反対に、外部パラメータを使ったカメラ座標系から世界座標系への変換したい場合の手順は、こちらの記事で解説しています。&lt;/p&gt;
&lt;p&gt;&amp;lt;LinkCard url=&quot;/2018/10/28/post-962/&quot; /&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>