<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://codedchords.dev/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https://codedchords.dev</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>
            This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It's free.
        </tabi:about_feeds>
        <tabi:visit_the_site>
            Visit website
        </tabi:visit_the_site>
        <tabi:recent_posts>
            Recent posts
        </tabi:recent_posts>
        <tabi:last_updated_on>
            Updated on $DATE
        </tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>CLI</tabi:current_section>
    </tabi:metadata><title>Coded Chords - CLI</title>
        <subtitle>A personal blog</subtitle>
    <link href="https://codedchords.dev/tags/cli/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://codedchords.dev/tags/cli/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2026-11-03T08:53:54+09:00</updated><id>https://codedchords.dev/tags/cli/atom.xml</id><entry xml:lang="ja">
        <title>ハッカー風ターミナルでブルーウォーカー気分を</title>
        <published>2026-11-03T08:53:54+09:00</published>
        <updated>2026-11-03T08:53:54+09:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2026/11/hackers-terminal/" type="text/html"/>
        <id>https://codedchords.dev/blog/2026/11/hackers-terminal/</id>
        
            <content type="html">&lt;!-- textlint-disable --&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/11/hackers-terminal/cover.webp?h=b90e56642808cef350a1&quot; alt=&quot;Cover&quot; width=&quot;1800&quot; height=&quot;707&quot; loading=&quot;lazy&quot; /&gt;
&lt;!-- textlint-enable --&gt;
&lt;details&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;!-- toc --&gt;
&lt;/details&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;
&lt;p&gt;ドラマ『VIVANT』シーズン2は最終回を残すのみとなり、個人的に盛り上がっております。中でも凄腕ハッカーのBlue@walkerは、エンジニア界隈でも人気のようです。&lt;/p&gt;
&lt;p&gt;そこで、ハッカー風ターミナルを表示する設定を自分でも作ってみました。&lt;/p&gt;
&lt;h2 id=&quot;意外にちゃんと作られているBlue@walkerのターミナル&quot;&gt;意外にちゃんと作られているBlue@walkerのターミナル&lt;/h2&gt;
&lt;p&gt;シーンによっては、Blue@walkerが&lt;code&gt;$ssh bluewalker[Enter]&lt;/code&gt;と打っただけで「ハッキングできました」と言うなど、噴飯ものの表現もあり、ネタにされていました。しかし、Blue@walkerが使う画面をよく見ると、意外にしっかり作り込まれています。&lt;/p&gt;
&lt;p&gt;画面は、開発ツールのAndroid Studioと、おそらく独自に作った表示を合成したもので、独自の表示にはARP偽装やBGP乗っ取りのスクリプトらしきものが流れています。Pythonのコードには、劇中の進行に合わせたかのようなコメントまで書かれています。エディタには、暗号化メッセージツールSignalのコードだったり、既知の脆弱性に関連すると思われるコードだったりが開かれており、なにか良からぬ取り組みを思わせます。プロセスの表示にはbashtopが使われています。&lt;/p&gt;
&lt;p&gt;IPアドレスにはプライベートアドレスを使い、MACアドレスも明らかに連番と分かるダミーにするなど、プライバシーへの配慮もうかがえます。SSHの場面は笑ってしまいましたが、画面そのものはそれなりに作られています。&lt;/p&gt;
&lt;h2 id=&quot;設計方針&quot;&gt;設計方針&lt;/h2&gt;
&lt;p&gt;偽のビルドログやダウンロード進捗を延々と流す&lt;code&gt;genact&lt;/code&gt;や、映画『マトリックス』風の画面を表示する&lt;code&gt;cmatrix&lt;/code&gt;といったツールを使えば、それらしいターミナル画面は作れます。しかし今回は、自分にとって意味のある情報を表示したいと考え、次の方針を立てました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;意味のない表示はしない&lt;/li&gt;
&lt;li&gt;画面分割はWezTermの機能で行う&lt;/li&gt;
&lt;li&gt;ウィンドウは2つ使う&lt;/li&gt;
&lt;li&gt;シェルコマンド1つで起動できる&lt;/li&gt;
&lt;li&gt;管理者権限は使わない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ウィンドウを2つに分けたのは、画面いっぱいに表示しつつ、後からレイアウトを変えられる余地を残すためです。画面構成は次のとおりです。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ウィンドウ 1（監視）                      ウィンドウ 2（株価・ニュース）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ┌──────────────────┬──────────────────┐   ┌──────────────────┬──────────────────┐&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  │ btm              │ gping            │   │ tickrs (summary) │ sysnews          │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  │                  ├──────────────────┤   │                  │                  │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ├──────────────────┤ wtfutil          │   │                  │                  │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  │ macmon           │                  │   │                  │                  │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  └──────────────────┴──────────────────┘   └──────────────────┴──────────────────┘&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;ウィンドウ1の説明&quot;&gt;ウィンドウ1の説明&lt;/h2&gt;
&lt;p&gt;ウィンドウ1は主にシステム関連情報を表示します。&lt;/p&gt;
&lt;h3 id=&quot;btm&quot;&gt;btm&lt;/h3&gt;
&lt;p&gt;btmは、Rustで書かれたターミナル向けのシステムモニターです（プロジェクト名はbottom）。topやhtopと同じ系統のツールで、使用量の推移をグラフで表示できます。&lt;/p&gt;
&lt;p&gt;CPU・メモリ（キャッシュメモリを含む）・温度・プロセス・ディスク・ネットワーク帯域を1つの画面にまとめて表示するので、資源の使用量と、それを使っているプロセスを見比べられます。Mac全体の負荷を一目でつかめる、ウィンドウ1の中心となる表示です。&lt;/p&gt;
&lt;!-- textlint-disable --&gt;
&lt;figure&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/11/hackers-terminal/btm.webp?h=2f247b7b0a41ed0341fc&quot; alt=&quot;btm の画面。CPU・メモリ・ネットワークのグラフ、温度、ディスク、プロセス一覧&quot; width=&quot;400&quot; height=&quot;319&quot; loading=&quot;lazy&quot; /&gt;
&lt;figcaption&gt;btm の表示&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;!-- textlint-enable --&gt;
&lt;h3 id=&quot;macmon&quot;&gt;macmon&lt;/h3&gt;
&lt;p&gt;macmonは、Apple Silicon搭載のMac向けのモニターツールで、消費電力・温度・GPUの状態を表示します。btmが見せるのはOSから見た資源の使用量で、macmonが見せるのはチップそのものの状態です。&lt;/p&gt;
&lt;p&gt;同じ情報はmacOS標準のpowermetricsでも取れますが、こちらは管理者権限が必要です。macmonはsudoなしで動くため、常に表示しておけます。btmと見比べれば、CPUの負荷が電力や温度にどう表れているかを確かめられます。&lt;/p&gt;
&lt;!-- textlint-disable --&gt;
&lt;figure&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/11/hackers-terminal/macmon.webp?h=dafe88243016f05fb8dc&quot; alt=&quot;macmon の画面。E-CPU・P-CPU・GPU の使用率と電力、メモリ、プロセスごとの電力&quot; width=&quot;400&quot; height=&quot;265&quot; loading=&quot;lazy&quot; /&gt;
&lt;figcaption&gt;macmon の表示&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;!-- textlint-enable --&gt;
&lt;h3 id=&quot;gping&quot;&gt;gping&lt;/h3&gt;
&lt;p&gt;gpingは、pingの応答時間をグラフで表示するツールです。複数の宛先に同時にpingを打ち、結果を1つのグラフに重ねて描けます。&lt;/p&gt;
&lt;p&gt;宛先は、自宅のルーター（デフォルトゲートウェイ）と、Cloudflareの公開DNSサーバーである1.1.1.1の2か所です。通信が遅い、あるいは途切れたと感じたときは、2つの応答を比べれば原因のある場所を見分けられます。ルーターへの応答も乱れていれば原因は自宅内（ルーターやLAN）に、ルーターへの応答は安定していて1.1.1.1だけが乱れていれば自宅の外にあります。直近10分間の推移がグラフに残るので、少し前に起きた遅延や途切れにも後から気づけます。&lt;/p&gt;
&lt;h3 id=&quot;wtfutil&quot;&gt;wtfutil&lt;/h3&gt;
&lt;p&gt;wtfutil（WTF）は、ターミナル上にダッシュボードを作るツールです。天気・時計・Hacker Newsなどのモジュールが用意されていて、表示したいものを選んで1つの画面に並べられます。&lt;/p&gt;
&lt;p&gt;ここには、システム監視以外で日頃確かめたい情報を集めました。東京の天気に加えて、東京・ニューヨーク・ロンドン・ロサンゼルスの現在時刻を表示しています。ウィンドウ2では米国株の値動きや海外のニュースを見るので、現地がいま何時かが分かれば、市場が開いている時間かどうかや、ニュースがいつ出たものかの見当がつきます。&lt;/p&gt;
&lt;p&gt;グローバルIPアドレスと回線事業者も表示しています。VPNを経由しているか、どの回線から接続しているかがすぐ分かるので、gpingで通信の乱れに気づいたときの手がかりにもなります。バッテリーの残量と残り時間は、macmonの消費電力と合わせて見ると、負荷がバッテリーの減り方にどう響くかが分かります。Hacker Newsの見出しは、ウィンドウ2の時事ニュースでは拾いきれない技術系の話題を補います。&lt;/p&gt;
&lt;h2 id=&quot;ウィンドウ2の説明&quot;&gt;ウィンドウ2の説明&lt;/h2&gt;
&lt;p&gt;ウィンドウ2は株価とニュースを表示します。ウィンドウ1がこのMacと回線の状態を見せるのに対し、こちらは世の中の動きを見せます。&lt;/p&gt;
&lt;h3 id=&quot;tickrs&quot;&gt;tickrs&lt;/h3&gt;
&lt;p&gt;tickrsは、ターミナルで株価のチャートを表示するツールで、データはYahoo! Financeから取得します。Apple・Amazon・IBM・Palantir・Tesla・Alphabetの6銘柄の値動きを、1銘柄数行の要約表示で並べています。銘柄はあくまでサンプルで、エンジニアが興味を持ちそうなものを適当に選びました。&lt;/p&gt;
&lt;p&gt;ブラウザや証券会社のアプリを開かなくても、注目している銘柄の動きを目の端で追えます。ハッカー風の画面に流れる数字が架空のものではなく実際の相場であることも、意味のない表示はしないという今回の方針に合っています。&lt;/p&gt;
&lt;h3 id=&quot;sysnews&quot;&gt;sysnews&lt;/h3&gt;
&lt;p&gt;sysnewsは自作のスクリプトで、BBCとNYT、ついでにNHKとYahoo!ニュースの新着の見出しを、時刻順に流し続けます。日本のメディアの偏向具合が酷いので、海外の報道もあわせてウォッチしておきたいと考えました。国内と海外のメディアを並べておけば、同じ時間帯に国内外で何が起きているかを一覧できます。&lt;/p&gt;
&lt;p&gt;英語の見出しの翻訳と、私の関心事に合う見出しの強調にはClaude Codeを使っています。APIではなく、サブスクリプションで利用しているClaude Codeを呼び出す形にしています。モデルは軽量のHaikuを指定しています。&lt;/p&gt;
&lt;p&gt;これで、海外のニュースも日本語で追えます。関心事に合う見出しは★付きの黄色で強調されるので、流れてくる見出しの中から自分に関係のあるものを見落としにくくなります。&lt;/p&gt;
&lt;p&gt;見出しをクリックするとブラウザで記事が開き、あとから履歴を選んで開いたり、org-roamに保存したりもできます。見出しを眺めるだけで終わらず、気になった記事へすぐたどり着けます。&lt;/p&gt;
&lt;h2 id=&quot;コード解説&quot;&gt;コード解説&lt;/h2&gt;
&lt;p&gt;今回の設定とスクリプト（sysmon・sysmon.yml・sysnews・tenki）は、すべてGistで公開しています。ここでは要点だけを抜き出して解説します。&lt;/p&gt;
&lt;!-- textlint-disable --&gt;
&lt;iframe class=&quot;hatenablogcard&quot; src=&quot;https://hatenablog-parts.com/embed?url=https://gist.github.com/yostos/4a8365333ec0c29b686a3af36c3d720a&quot; width=&quot;100%&quot; height=&quot;155&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;!-- textlint-enable --&gt;
&lt;h3 id=&quot;WezTermの制御&quot;&gt;WezTermの制御&lt;/h3&gt;
&lt;p&gt;ウィンドウとペインの組み立てには、WezTermに付属する &lt;code&gt;wezterm cli&lt;/code&gt; を使っています。起動中のWezTermを外から操作するコマンドで、シェルスクリプトから新しいウィンドウを開いたり、ペインを分割したりできます。起動スクリプトsysmonのうち、2つのウィンドウを組み立てる部分は次のとおりです。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot; data-name=&quot;sysmon&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# ウィンドウ 1: 監視（左 btm / macmon、右 gping / wtfutil）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;P&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli spawn --new-window --&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  gping -n &amp;quot;&lt;/span&gt;&lt;span&gt;$PING_INTERVAL&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -b &amp;quot;&lt;/span&gt;&lt;span&gt;$PING_WINDOW&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span&gt;${ping_targets&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;B&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli split-pane --pane-id &amp;quot;&lt;/span&gt;&lt;span&gt;$P&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --left --percent&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 50&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  btm --enable_gpu --theme nord --enable_cache_memory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli split-pane --pane-id &amp;quot;&lt;/span&gt;&lt;span&gt;$B&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --bottom --percent&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 45&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -- macmon&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;/dev/null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli split-pane --pane-id &amp;quot;&lt;/span&gt;&lt;span&gt;$P&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --bottom --percent &amp;quot;&lt;/span&gt;&lt;span&gt;$WTF_PERCENT&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  wtfutil --config=&amp;quot;&lt;/span&gt;&lt;span&gt;$WTF_CONFIG&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;/dev/null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# ウィンドウ 2: 株価（左。右上のヘルプ表示は隠す）とニュース（右）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;T&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli spawn --new-window --&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  tickrs --summary -s &amp;quot;&lt;/span&gt;&lt;span&gt;$WATCHLIST&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -i &amp;quot;&lt;/span&gt;&lt;span&gt;$STOCK_INTERVAL&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --hide-help&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;wezterm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; cli split-pane --pane-id &amp;quot;&lt;/span&gt;&lt;span&gt;$T&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; --right --percent&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 50&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;  sysnews -i &amp;quot;&lt;/span&gt;&lt;span&gt;$NEWS_INTERVAL&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;/dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;wezterm cli spawn --new-window&lt;/code&gt; は、新しいウィンドウを開いて指定したコマンドを起動し、そのペインのIDを出力します。&lt;code&gt;wezterm cli split-pane&lt;/code&gt; は、&lt;code&gt;--pane-id&lt;/code&gt; で指定したペインを &lt;code&gt;--left&lt;/code&gt; や &lt;code&gt;--bottom&lt;/code&gt; の方向に分割し、新しいペインの大きさを &lt;code&gt;--percent&lt;/code&gt; の割合で決めます。分割したペインをさらに分割するときのために、返ってきたIDを変数に受け取っておきます。ウィンドウ1では、gpingのペイン（P）を左に分割してbtmを置き、btmのペイン（B）を下に分割してmacmonを、gpingのペインを下に分割してwtfutilを置いています。この先使わないIDは &lt;code&gt;/dev/null&lt;/code&gt; に捨てています。&lt;/p&gt;
&lt;p&gt;ただし、&lt;code&gt;wezterm cli&lt;/code&gt; ではウィンドウの位置や大きさを指定できません。2つのウィンドウを画面のどこに置くかは、起動したあとに手で調整しています。&lt;/p&gt;
&lt;h3 id=&quot;Claude_Codeの呼び出し&quot;&gt;Claude Codeの呼び出し&lt;/h3&gt;
&lt;p&gt;sysnewsの要は、Claude Codeの &lt;code&gt;claude -p&lt;/code&gt;（非対話モード）を、翻訳と判定を受け持つ部品として呼び出しているところです。新しく取得した見出しをまとめてJSONで渡し、見出しごとに日本語訳と、関心事に当たるかどうかの判定を返してもらいます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot; data-name=&quot;sysnews&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cmd&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;claude&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;-p&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--setting-sources&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--tools&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--no-session-persistence&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--model&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; CLAUDE_MODEL&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--output-format&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;json&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--json-schema&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;SCHEMA&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--system-prompt&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; system_prompt&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;interests&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;--&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; ensure_ascii&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ポイントは、&lt;code&gt;--json-schema&lt;/code&gt; で出力の形を決めていることです。見出しごとに、日本語の見出し（&lt;code&gt;ja&lt;/code&gt;）、概要の訳（&lt;code&gt;desc_ja&lt;/code&gt;）、関心事に当たるか（&lt;code&gt;hit&lt;/code&gt;）、当たる理由（&lt;code&gt;why&lt;/code&gt;）を返すようスキーマで指定しているので、返ってきた結果をそのままプログラムで扱えます。翻訳と判定を1回の呼び出しで済ませ、見出しもまとめて渡すので、呼び出しの回数も抑えられます。また、&lt;code&gt;--tools &quot;&quot;&lt;/code&gt; と &lt;code&gt;--setting-sources &quot;&quot;&lt;/code&gt; でツールも手元の設定も使わず、&lt;code&gt;--no-session-persistence&lt;/code&gt; で会話の履歴も残しません。毎回まっさらな状態で、決まった仕事だけをさせる使い方です。&lt;/p&gt;
&lt;p&gt;関心事は &lt;code&gt;interests.md&lt;/code&gt; に自由な文章で書いておき、その内容をシステムプロンプトに埋め込んでいます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot; data-name=&quot;sysnews&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span&gt; interests&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    p&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;- hit: 見出しと概要が、下の「注目する内容」のどれかに当てはまれば true。&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;はっきり関係するものだけを true にし、迷うものは false にする。&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;- why: hit が true のとき、どの項目に当たるかを 15 字以内の日本語で。&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;false なら空文字。&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;        f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;注目する内容:&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\n{&lt;/span&gt;&lt;span&gt;interests&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}\n&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;キーワードの一致で拾うのではなく、見出しと概要の意味からLLMが判断するので、関心事に書いた言葉が見出しに含まれていなくても、関係する記事を拾えます。一方で、何でも強調されてしまっては意味がありません。「迷うものはfalseにする」と指示して、はっきり関係するものだけに絞っています。&lt;/p&gt;
&lt;p&gt;見出しをクリックするとブラウザが開くのは、OSC 8というエスケープシーケンスを使っているためです。OSC 8は、ターミナルへ表示する文字列にリンク先のURLを埋め込む仕組みです。WezTermなど対応しているターミナルでは、表示した文字列がそのままクリックできるリンクになります。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot; data-name=&quot;sysnews&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; link&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; str&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; text&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; str&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;) -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; str&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;OSC 8 ハイパーリンク&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\033&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;]8;;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}\033\\{&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}\033&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;]8;;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\033\\&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表示した見出しは履歴としてファイルに残しておき、&lt;code&gt;p&lt;/code&gt; キーでfzfの選択画面を開けるようにしています。プレビュー欄で概要（英語の記事は訳と原文）を確かめてから、Enterを押すとブラウザで開き、Ctrl-Bを押すとEmacsのノート管理パッケージorg-roamに保存できます。保存すると記事1件につき1つのノードを作り、記事のURLと概要を書き込みます。同じURLのノードがすでにあれば、重ねては作りません。流れていった見出しにも、あとから戻れます。&lt;/p&gt;
&lt;p&gt;Claude Codeの呼び出しに失敗したときは、翻訳と強調をせず、元の見出しのまま表示を続けます。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;見た目のそれらしさだけなら、genactやcmatrixで十分です。今回は「意味のない表示はしない」を方針に、既存のターミナルツールと自作のスクリプトを組み合わせて、ハッカー風の画面を作りました。&lt;/p&gt;
&lt;p&gt;ウィンドウ1ではこのMacと回線の状態を、ウィンドウ2では株価とニュースという世の中の動きを表示します。どのツールも管理者権限なしで動き、&lt;code&gt;wezterm cli&lt;/code&gt; で組み立てているので、シェルコマンド1つで2つのウィンドウがそろいます。sysnewsではClaude Codeを翻訳と判定の部品として使い、海外のニュースを日本語で追いながら、関心のある見出しを見落とさないようにしました。&lt;/p&gt;
&lt;p&gt;画面に流れているのは、作り物のログではなく、実際の数値と見出しです。設定とスクリプトはGistで公開しているので、表示するツールを差し替えて、自分にとって意味のある画面に組み替えてみてください。&lt;/p&gt;
</content>
        <summary type="html">ドラマ『VIVANT』のBlue@walkerのようなハッカー風のターミナル画面は、genact や cmatrix を使えば手軽に作れます。ただ、そこに流れているのは意味のない偽のログです。同じ画面を、自分にとって本当に役立つ情報で埋められないか、WezTerm と手持ちのツールで試しました。
</summary>
        </entry><entry xml:lang="ja">
        <title>gyrotriage — DJIドローン映像のブレを診断し、Gyroflowのパラメータを自動推定するツール</title>
        <published>2026-03-07T22:00:00+09:00</published>
        <updated>2026-06-04T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2026/03/gyrotriage/" type="text/html"/>
        <id>https://codedchords.dev/blog/2026/03/gyrotriage/</id>
        
            <content type="html">&lt;!-- textlint-disable --&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/03/gyrotriage/cover.webp?h=a52eaf44e6e2f027bce2&quot; alt=&quot;gyrotriage HUD出力例&quot; width=&quot;1440&quot; height=&quot;900&quot; loading=&quot;lazy&quot; /&gt;
&lt;!-- textlint-enable --&gt;
&lt;details&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;!-- toc --&gt;
&lt;/details&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;/span&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;（2026-06-04更新）&lt;/strong&gt; 本記事は当初、Gyroflowスタンドアロンアプリ向けの5つのパラメータを推奨する内容でした。その後、実運用のワークフローがDaVinci Resolve上のGyroflow OpenFXプラグインで完結することを踏まえ、推奨対象をプラグイン版「Adjust parameters」に一本化しました。以下では旧スタンドアロンアプリ版の記述に取り消し線を引き、新しい内容を併記しています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;Gyroflowのパラメータ調整は面倒である&quot;&gt;Gyroflowのパラメータ調整は面倒である&lt;/h2&gt;
&lt;p&gt;DJI NeoやAvataシリーズでFPV撮影をしている方なら、Gyroflowでの後処理は日常的な作業だと思います。EISをオフにして撮影し、Gyroflowでスタビライズする。この流れ自体は確立されていますが、問題はGyroflowのパラメータ調整です。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;Gyroflowには主に5つの調整パラメータがあります。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;DaVinci ResolveなどでGyroflow OpenFXプラグインを使う場合、調整の中心になるのは「Adjust parameters」パネルのSmoothnessとZoom limitです。スタンドアロンアプリ版にあった細かなパラメータはプラグインのUIには現れず、&lt;code&gt;.gyroflow&lt;/code&gt; プロジェクト側に焼き込まれます（2026-06-04更新）。&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;Smoothness (1〜300)&lt;/td&gt;&lt;td&gt;スムージングの強度。高いほどブレを強く除去するが、意図した動きも鈍る&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;del&gt;Max smoothness (s)&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;突発的なブレに対する最大補正量&lt;/del&gt;（プラグイン版では非対応）&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;del&gt;Max smoothness at high velocity (s)&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;高速旋回時の補正上限。短くすると旋回への追従性が上がる&lt;/del&gt;（プラグイン版では非対応）&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Zoom limit (%)&lt;/td&gt;&lt;td&gt;安定化で生じる黒縁を隠すためのズーム上限&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;del&gt;Zooming speed (s)&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;Dynamic zoomingのFOV遷移速度&lt;/del&gt;（プラグイン版では非対応）&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;FOV&lt;/td&gt;&lt;td&gt;画角。算出対象外でベースライン1.0を提示&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;プラグインのSmoothnessは1〜300のスケール（デフォルト50）で、gyrotriageが従来出力していた百分率の値をそのまま入力できます。強く設定すればブレは消えますが、意図したカメラワークまで平滑化されたり、クロップで解像度が落ちたりします。適切な値はクリップごとに異なるため、毎回プレビューを見ながら試行錯誤することになります。&lt;/p&gt;
&lt;p&gt;1本2本ならまだしも、フライト後に何十本ものクリップを処理するときは正直つらい作業です。&lt;/p&gt;
&lt;h2 id=&quot;gyrotriageでパラメータ推定を自動化する&quot;&gt;gyrotriageでパラメータ推定を自動化する&lt;/h2&gt;
&lt;!-- textlint-disable --&gt;
&lt;iframe class=&quot;hatenablogcard&quot; src=&quot;https://hatenablog-parts.com/embed?url=https://github.com/yostos/gyrotriage&quot; width=&quot;100%&quot; height=&quot;155&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;!-- textlint-enable --&gt;
&lt;p&gt;そこで作ったのが &lt;strong&gt;gyrotriage&lt;/strong&gt; です。DJIドローンのMP4ファイルを渡すだけで、埋め込まれたモーションデータを解析し、ブレの度合いをスコアリングしつつ、&lt;del&gt;Gyroflowの5つのパラメータ&lt;/del&gt;Gyroflow OpenFXプラグイン（DaVinci Resolve等）に入力するパラメータを自動で推定します。&lt;/p&gt;
&lt;p&gt;考え方はシンプルです。パイロットが意図したカメラワーク（パン・チルト・旋回）と除去したいブレ（風や機体振動）では、動きの周波数帯域が異なります。ゆっくりしたパンは1Hz以下の低周波、プロペラ振動は20Hz以上の高周波に現れます。この境界を信号処理で自動検出し、Gyroflowの内部ロジックに合わせてパラメータに変換しています。&lt;/p&gt;
&lt;h2 id=&quot;インストール&quot;&gt;インストール&lt;/h2&gt;
&lt;p&gt;macOSの場合はHomebrewでインストールできます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install gyrotriage&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rustの開発環境がある場合はcargoからもインストールできます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;cargo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install --path .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;使い方&quot;&gt;使い方&lt;/h2&gt;
&lt;p&gt;基本的な使い方はMP4ファイルを渡すだけです。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gyrotriage&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; DJI_0001.MP4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以下のようなテキスト出力が得られます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File:        DJI_20260228080801_0003_D.MP4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Duration:    30.3s (60593 samples @ 2000Hz)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Score:       100 / 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Level:       SEVERE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;RMS:         34.8 °/s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Peak:        644.0 °/s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Pitch:       avg=1.4°/s  std=2.2°/s  max=47.9°/s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Roll:        avg=11.7°/s  std=13.5°/s  max=251.1°/s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Yaw:         avg=3.3°/s  std=4.4°/s  max=89.0°/s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Gyroflow plugin (Adjust parameters):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Smoothness:           21&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Zoom limit:           118&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  FOV:                  1.000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Integration method:   None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Lens correction:      100&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;末尾の「Gyroflow plugin (Adjust parameters)」ブロックが、Gyroflow OpenFXプラグインの「Adjust parameters」パネルにそのまま入力できる推奨値です。&lt;del&gt;最後の2行がGyroflowに入力する推定パラメータです。&lt;/del&gt; このうちSmoothnessとZoom limitはモーション解析から算出した値、Integration methodとLens correctionはDJI機の特性に基づく固定推奨です。この値をプラグインのパネルに入力すれば、そこから微調整を始められます（2026-06-04更新）。&lt;/p&gt;
&lt;p&gt;なお旧スタンドアロンアプリ版では、末尾の出力が &lt;del&gt;&lt;code&gt;Gyroflow: smoothness=21% max=0.300s max@hv=0.030s zoom_limit=118% zooming_speed=2.6s&lt;/code&gt;&lt;/del&gt; という5値の形式でした。&lt;/p&gt;
&lt;h2 id=&quot;HUD表示で直感的にブレを把握する&quot;&gt;HUD表示で直感的にブレを把握する&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--visual&lt;/code&gt; オプションを付けると、ブレの特性をHUD風のグラフィックで表示します。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gyrotriage&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; DJI_0001.MP4 --visual&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;冒頭のカバー画像がHUD出力の実例です。画面には以下の情報が表示されます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;スコアゲージ&lt;/strong&gt;: ブレの重大度を0〜100で表示（STABLE / MILD / MODERATE / SEVEREの4段階）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;レーダーチャート&lt;/strong&gt;: Pitch・Roll・Yawの3軸でブレの偏りを可視化&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;del&gt;Gyroflowに入力する5つの値&lt;/del&gt;Gyroflow OpenFXプラグインに入力する推奨値&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数値だけでは分かりにくい「このクリップはRoll方向のブレが支配的」「後半に激しいブレが集中している」といった特性が一目で把握できます。Gyroflowプラグインを適用する前に、どのクリップにどの程度の処理が必要かをトリアージするのに役立ちます。&lt;/p&gt;
&lt;p&gt;HUD画像はPNGファイルとしても出力できます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gyrotriage&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; DJI_0001.MP4 --output-image shake.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WezTermやiTerm2などSixel/iTerm2プロトコルに対応したターミナルでは &lt;code&gt;--visual&lt;/code&gt; でインライン表示されます。非対応のターミナルでは &lt;code&gt;--output-image&lt;/code&gt; でファイル出力してください。&lt;/p&gt;
&lt;p&gt;テキストのみの環境向けには &lt;code&gt;--sparkline&lt;/code&gt; オプションもあります。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gyrotriage&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; DJI_0001.MP4 --sparkline&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;スコアの読み方&quot;&gt;スコアの読み方&lt;/h2&gt;
&lt;p&gt;gyrotriageはブレの重大度を0〜100のスコアで表示します。&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;STABLE&lt;/td&gt;&lt;td&gt;0〜25&lt;/td&gt;&lt;td&gt;ブレはほぼなし。スタビライズ不要&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MILD&lt;/td&gt;&lt;td&gt;26〜50&lt;/td&gt;&lt;td&gt;軽微なブレ。スタビライズは任意&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;MODERATE&lt;/td&gt;&lt;td&gt;51〜75&lt;/td&gt;&lt;td&gt;目に見えるブレ。Gyroflow推奨&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SEVERE&lt;/td&gt;&lt;td&gt;76〜100&lt;/td&gt;&lt;td&gt;激しいブレ。Gyroflow必須&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;DJI Air 3Sのような3軸ジンバル搭載機ではほぼSTABLEになりますが、DJI Neoのような1軸ジンバル機やAvataシリーズでアクロバティックな飛行をした場合はMODERATE〜SEVEREになることが多いです。&lt;/p&gt;
&lt;h2 id=&quot;推定パラメータの使い方&quot;&gt;推定パラメータの使い方&lt;/h2&gt;
&lt;p&gt;~~出力された5つのパラメータはGyroflowのUIに直接入力できます。~~出力された推奨値はGyroflow OpenFXプラグインの「Adjust parameters」パネルに直接入力できます（2026-06-04更新）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;del&gt;Gyroflowでクリップを開く&lt;/del&gt;DaVinci ResolveのタイムラインでクリップにGyroflowプラグインを適用する&lt;/li&gt;
&lt;li&gt;出力された値をそれぞれのスライダーに入力する&lt;/li&gt;
&lt;li&gt;プレビューで確認し、必要に応じて微調整する&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;このとき、Integration methodはNone、Lens correctionは100、FOVは1.0を起点にします。DJI機はクォータニオンを記録済みなので再積分が不要で、正しいレンズプロファイルを読み込めばフル補正で問題ないためです。&lt;/p&gt;
&lt;p&gt;推定値はあくまで調整の出発点です。ブレが残っている場合はSmoothnessを上げる、カメラワークが鈍く感じる場合はSmoothnessを下げる、といった微調整で仕上げてください。ゼロからスライダーを動かして探るよりも、妥当な出発点があるだけで調整時間は大幅に短縮できます。&lt;/p&gt;
&lt;h2 id=&quot;対応機体と撮影条件&quot;&gt;対応機体と撮影条件&lt;/h2&gt;
&lt;p&gt;モーションデータがMP4へ埋め込まれるDJI機種に対応しています。ただし撮影時の設定次第ではモーションデータが記録されないため、注意が必要です。&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;DJI Avata / Avata 2&lt;/td&gt;&lt;td&gt;EIS（Rocksteady）オフ、FOV Wide&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DJI Neo / Neo2&lt;/td&gt;&lt;td&gt;アスペクト比4:3（EISは自動的にオフ）&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Avataシリーズの場合、Rocksteadyをオンにしているとモーションデータが記録されません。Gyroflowで後処理する前提なら、撮影時はRocksteadyオフ・FOV Wideで撮影してください。&lt;/p&gt;
&lt;p&gt;DJI Neoの場合、16:9で撮影するとモーションデータが記録されません。4:3で撮影する必要があります。&lt;/p&gt;
&lt;p&gt;DJI Air 3Sなど3軸ジンバル搭載機はGyroflow自体を必要としないケースが多く、対象外としています。&lt;/p&gt;
&lt;h2 id=&quot;実際の効果&quot;&gt;実際の効果&lt;/h2&gt;
&lt;p&gt;gyrotriageで推定したパラメータを微調整なしでそのままGyroflow OpenFXプラグインに適用した映像（右）と、オリジナルの未処理映像（左）の比較です。&lt;/p&gt;
&lt;!-- textlint-disable --&gt;
&lt;div class=&quot;youtube-container&quot; style=&quot;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%;&quot;&gt;
  &lt;iframe
    src=&quot;https://www.youtube-nocookie.com/embed/uEInUgQIBrs&quot;
    style=&quot;position: absolute; top: 0; left: 0; width: 100%; height: 100%;&quot;
    frameborder=&quot;0&quot;
    allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
    allowfullscreen&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;!-- textlint-enable --&gt;
&lt;h2 id=&quot;注意事項&quot;&gt;注意事項&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;推定値は信号処理に基づく客観的な算出であり、映像の主観的な品質評価は含まない&lt;/li&gt;
&lt;li&gt;非常にゆっくりしたブレ（0.3Hz未満）は意図した動きと区別できない&lt;/li&gt;
&lt;li&gt;GyroflowのDefaultアルゴリズムを前提としており、Plain 3Dや軸別モードでは推定値の解釈が異なる&lt;/li&gt;
&lt;li&gt;極端に短いクリップ（2秒未満）では推定精度が低下する&lt;/li&gt;
&lt;li&gt;商用空撮など業務用途では、必ずGyroflowのプレビューで結果を検証すること&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- textlint-disable --&gt;
&lt;div class=&quot;references&quot;&gt;
    &lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://gyroflow.xyz/&quot;&gt;Gyroflow&lt;/a&gt;. 「Gyroflow - Video stabilization using gyroscope data」&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;!-- textlint-enable --&gt;
</content>
        <summary type="html">Gyroflowのパラメータ調整に毎回悩んでいませんか。gyrotriageはDJIドローンのMP4ファイルからブレを診断し、DaVinci ResolveのGyroflow OpenFXプラグインに入力するパラメータを自動で推定するCLIツールです。導入方法から使い方、出力の読み方までを解説します。</summary>
        </entry><entry xml:lang="ja">
        <title>もしかしてだけど、あれって日本最古のアスキーアートだったんじゃないの？</title>
        <published>2026-02-21T00:00:00+00:00</published>
        <updated>2026-02-21T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/" type="text/html"/>
        <id>https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/</id>
        
            <content type="html">&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/cover.webp?h=23adb599ec98a887ae4f&quot; alt=&quot;img2charによる変換例&quot; width=&quot;800&quot; height=&quot;485&quot; loading=&quot;lazy&quot; /&gt;
&lt;p&gt;&lt;em&gt;左の画像はMidjourneyで生成したものです&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;1986年、パソコン通信は黎明期でASCIInetがまだ実験運用の時代でした。その頃の私はグラフィック（おそらくモノクロの有名歌手の画像）を自作プログラムでASCII文字の絵にして、あるパソコン通信のBBS（電子掲示板）に投稿しました。&lt;/p&gt;
&lt;p&gt;あれから約40年。当時のアルゴリズムをGoで再実装し、CLIツール &lt;code&gt;img2char&lt;/code&gt; として公開しました。&lt;/p&gt;
&lt;iframe class=&quot;hatenablogcard&quot; src=&quot;https://hatenablog-parts.com/embed?url=https://github.com/yostos/img2char&quot; width=&quot;100%&quot; height=&quot;155&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;この記事では、当時の背景と &lt;code&gt;img2char&lt;/code&gt; のアルゴリズムを紹介します。&lt;/p&gt;
&lt;h2 id=&quot;1986年のパソコンとBBS&quot;&gt;1986年のパソコンとBBS&lt;/h2&gt;
&lt;p&gt;1986年はPC-VAN（1986年4月実験開始）やNIFTY-Serve（1987年4月開始）が立ち上がり始めた、日本のパソコン通信黎明期です。アスキーネット（ASCII-NET）は前年の1985年5月に実験サービスを開始しており、草の根BBSも各地で産声を上げていました。通信速度は300bpsから1200bps。BBSに書き込めるのはテキストだけの時代でした。グラフィックスを書き込むなんてとんでもない時代でした&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/#fn-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;当時のパソコンのグラフィック事情も触れておきます。1986年時点では、画像をファイルとして保存・交換する汎用フォーマットはまだ普及していませんでした。グラフィックを描くといえば、BASICのLINE文やPAINT文を使ってプログラムとして記述するのが主流です。私自身、PAINT文を使わずに画像の上端から下端まで（縦200ドットしかない画面です）をLINE文だけで1ラインずつ塗りつぶし、モノクロ画像を描くことに当時凝っていました。グラフィックは「描く」ものであって「ファイルで送る」ものではなかったのです。&lt;/p&gt;
&lt;p&gt;しかもBASICは機種ごとに方言が異なり、あるマシンで書いたプログラムが別のマシンでそのまま動くことはまずありません。グラフィック描画プログラムをBBSで流通させるのも現実的ではありませんでした。&lt;/p&gt;
&lt;p&gt;そこで思いついたのが、「ASCII文字絵にすれば遅いパソコン通信でも送れるんじゃね？」です。&lt;/p&gt;
&lt;h2 id=&quot;Sharp_X1_──_骨の髄までハードを極めたマシン&quot;&gt;Sharp X1 ── 骨の髄までハードを極めたマシン&lt;/h2&gt;
&lt;p&gt;Sharp X1は1982年に発売された8ビットパソコンです。テレビ事業で培ったシャープの技術が随所に活かされた、ハードウェア設計の塊のようなマシンでした。&lt;/p&gt;
&lt;p&gt;当時のZ80 CPUはメモリ空間が64KBしかなく、NECのPC-8801などではこの空間にメインRAMとグラフィックVRAM（GVRAM）を重ねて配置し、バンク切り替えでアクセスしていました。一方X1は、Z80のI/O命令が実行時にアドレスバスの上位8ビット（A8〜A15）にもレジスタの値を出力するという、公式マニュアルには記載されているものの目立たない仕様を活用しました。I/O空間を実質64KBに広げ、そこにGVRAMを配置したのです。メモリ空間64KBは丸ごとRAMとして使え、GVRAMへはI/O命令で直接アクセスできます。&lt;/p&gt;
&lt;p&gt;この設計のおかげで、BASICからでもグラフィックVRAM上の画像データを読み出すことができました。グラフィックVRAM上の画像を8x8ドット単位で読み取り、キャラクタROMの各文字パターンと比較する。一致度が最も高い文字を選ぶ。このアルゴリズムをBASICで実装しました&lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-2-1&quot;&gt;&lt;a href=&quot;https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/#fn-2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;h2 id=&quot;アルゴリズムの現代版_──_img2char&quot;&gt;アルゴリズムの現代版 ── img2char&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;img2char&lt;/code&gt; は、あの当時のアルゴリズムをGoで忠実に再現したCLIツールです。処理は3段階で構成されています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;フォントパターンの準備&lt;/strong&gt; ── 95個のASCII印字可能文字の8x8ビットマップを用意する&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;画像のブロック分割&lt;/strong&gt; ── 入力画像を8x8ピクセルのブロックに分割する&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;パターンマッチング&lt;/strong&gt; ── 各ブロックとフォントパターンのハミング距離を計算し、最も近い文字を選ぶ&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;フォントテーブルの構築&quot;&gt;フォントテーブルの構築&lt;/h3&gt;
&lt;p&gt;95個の印字可能ASCII文字（スペース &lt;code&gt;0x20&lt;/code&gt; からチルダ &lt;code&gt;0x7E&lt;/code&gt; まで）について、それぞれ8x8ドットのビットマップパターンを持っています。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;// font8x8Basic contains 8x8 bitmap patterns for 95 printable ASCII characters (0x20–0x7E).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;var&lt;/span&gt;&lt;span&gt; font8x8Basic&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;95&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // 0x20 &amp;#39; &amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    {&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // 0x21 &amp;#39;!&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    {&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3C&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3C&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // 0x22 &amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    {&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // ... 95文字分のパターンが続く&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // 0x41 &amp;#39;A&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    {&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0C&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1E&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3F&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    // 0x7E &amp;#39;~&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    {&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;6E&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3B&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; 0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;各バイトが8x8ビットマップの1行に対応し、LSB（最下位ビット）が左端のピクセルです。たとえば &lt;code&gt;A&lt;/code&gt; のパターンを展開すると、以下のようになります。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(2進数はLSB=左端ピクセルから表記)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x0C = 00110000 → ..##....&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x1E = 01111000 → .####...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x33 = 11001100 → ##..##..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x33 = 11001100 → ##..##..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x3F = 11111100 → ######..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x33 = 11001100 → ##..##..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x33 = 11001100 → ##..##..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;0x00 = 00000000 → ........&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;8バイトで1文字の形が表現されています。高速な比較のため &lt;code&gt;uint64&lt;/code&gt; にパックします。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; packFont&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; uint64&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    var&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; uint64&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        v&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |=&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; uint64&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;pattern&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; (&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; *&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    return&lt;/span&gt;&lt;span&gt; v&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;X1に搭載されていたHu-BASICの整数型は16ビット（2バイト）なので、当時は8バイトのパターンを2バイトずつ4回のXORループで比較していました。現代のGoでは8バイトを1つの64ビット整数に詰め込むことで、後述のXOR演算が一発で実行できます。&lt;/p&gt;
&lt;h3 id=&quot;8x8ブロックとフォントパターンの照合&quot;&gt;8x8ブロックとフォントパターンの照合&lt;/h3&gt;
&lt;p&gt;ここが核心部分です。画像から切り出した8x8ブロックを、95文字すべてのフォントパターンと比較し、最も「形が近い」文字を見つけます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; matchChar&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; fontTable&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;95&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;uint64&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; byte&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    blockVal&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; packBlock&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    bestDist&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 65&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt; // max possible is 64&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    bestIdx&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 95&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dist&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; :=&lt;/span&gt;&lt;span&gt; bits&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;OnesCount64&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;blockVal&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ^&lt;/span&gt;&lt;span&gt; fontTable&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; dist&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; bestDist&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            bestDist&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            bestIdx&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; i&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    return byte&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt;0x&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +&lt;/span&gt;&lt;span&gt; bestIdx&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;blockVal ^ fontTable[i]&lt;/code&gt; のXOR演算で、ブロックとフォントパターンのビットが異なる位置を検出します。&lt;code&gt;bits.OnesCount64&lt;/code&gt; でその異なるビット数（ハミング距離）を数えます。64ビット中の不一致ビット数が最小の文字を選ぶ、シンプルかつ強力な方式です。&lt;/p&gt;
&lt;h3 id=&quot;使い方&quot;&gt;使い方&lt;/h3&gt;
&lt;p&gt;入力画像は640x200または320x200ピクセルの二値化済みPNGである必要があります。前処理用のシェルスクリプトが付属しています。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 前処理（ImageMagickでリサイズ＋二値化）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./convert.sh&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.jpg&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 640 200&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# ASCII文字に変換&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;img2char&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input_640x200.png&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;640x200の画像は80x25文字、320x200なら40x25文字に変換されます。当時は320x200モードの40x25文字でやっていました。&lt;/p&gt;
&lt;p&gt;今回、同じロジックで似たような画像を変換してみましたが、なかなかいけています（冒頭の画像を参照）。&lt;/p&gt;
&lt;h2 id=&quot;もしかして......日本最古のASCII_Artなんじゃないの？&quot;&gt;もしかして......日本最古のASCII Artなんじゃないの？&lt;/h2&gt;
&lt;p&gt;当時の変換結果をパソコン通信のBBSに投稿したところ、ちょっとした話題になりました。テキストしか表示できない端末に、文字だけで画像が浮かび上がるのですから、当時としては驚きだったようです。40x25文字分の解像度しかありませんが、元の画像の輪郭や特徴が文字の組み合わせで再現される様子は、見た人を楽しませるものでした。&lt;/p&gt;
&lt;p&gt;ここで少し大胆なことを言わせてください。&lt;/p&gt;
&lt;p&gt;文字で画像を表現する試み自体は古く、1898年のタイプライターアートにまで遡ります。コンピューターによる自動変換も、1967年にベル研究所のケネス・ノウルトンがIBM 7094で実現しています。ただしそれは巨大なメインフレームでの話です。&lt;/p&gt;
&lt;p&gt;日本に目を向けると、記録に残る最古の「アスキーアート」は1986年6月20日にアスキーネット上で若林泰志が投稿した顔文字 &lt;code&gt;(^_^)&lt;/code&gt; とされています。2ちゃんねるでAAが花開くのは1999年以降。1986年当時、手作業でAA（アスキーアート）を描く文化はまだ広まっていませんでした。&lt;/p&gt;
&lt;p&gt;そんな時代に、8ビットパソコンで画像をASCII文字に変換してBBSに投稿していたわけです。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;もしかして、もしかしてだけど、これって日本最古のアスキーアートなんじゃないの？&lt;/strong&gt;（どぶろっく風）&lt;/p&gt;
&lt;p&gt;......まあ、証拠は当時のBBSのログとともに電子の海に消えてしまいましたが。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;img2char&lt;/code&gt; は、1986年のSharp X1で生まれたアイデアを現代のGoで再実装したツールです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当時のパソコン通信の制約（テキストのみ）が生んだ発想&lt;/li&gt;
&lt;li&gt;PCGという強力なハードウェアをあえて使わず、ASCII文字だけで勝負した選択&lt;/li&gt;
&lt;li&gt;8x8ドットブロックとフォントパターンのハミング距離による照合という、シンプルだが効果的なアルゴリズム&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;40年前のBASICプログラムが64ビット整数のXOR演算に姿を変えても、やっていることは同じです。画像の形を文字の形で近似する。それだけのことが、なぜか人を惹きつけます。&lt;/p&gt;
&lt;p&gt;興味を持っていただけたら、ぜひ試してみてください。&lt;/p&gt;
&lt;iframe class=&quot;hatenablogcard&quot; src=&quot;https://hatenablog-parts.com/embed?url=https://github.com/yostos/img2char&quot; width=&quot;100%&quot; height=&quot;155&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%B9%E3%82%AD%E3%83%BC%E3%82%A2%E3%83%BC%E3%83%88&quot;&gt;アスキーアート - Wikipedia&lt;/a&gt; -- 日本におけるアスキーアートの歴史、顔文字の起源&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://en.wikipedia.org/wiki/ASCII_art&quot;&gt;ASCII art - Wikipedia (English)&lt;/a&gt; -- タイプライターアート（1898年）からの世界的な歴史&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.asciiart.eu/history-of-ascii-art&quot;&gt;The history of ASCII Art - ASCII Art Archive&lt;/a&gt; -- ベル研のノウルトン（1967年）による自動変換の歴史&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://ja.wikipedia.org/wiki/X1_%28%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%29&quot;&gt;X1 (コンピュータ) - Wikipedia&lt;/a&gt; -- Sharp X1のハードウェア仕様&lt;/li&gt;
&lt;li&gt;Z80の16ビットI/Oアドレスについての考察（大石氏）-- Z80のI/O命令が16ビットアドレスを出力する仕様の解説（サイト閉鎖）&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://electrelic.com/electrelic/node/639&quot;&gt;パーソナルなコンピュータのメモリ事情（第3回：8ビット中期）- Electrelic&lt;/a&gt; -- X1とPC-8801のGVRAM配置方式の比較&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http://www.kogures.com/hitoshi/history/pc-tushin/index.html&quot;&gt;パソコン通信の歴史 - 木暮仁&lt;/a&gt; -- PC-VAN、NIFTY-Serve、草の根BBSの歴史&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- textlint-enable --&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;そもそも画像をファイルに書き出すフォーマットさえ標準的なものはなかった。 &lt;a href=&quot;https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/#fr-1-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;X1にはPCG（Programmable Character Generator）が搭載されており、文字のフォントパターンを自由に書き換えられました。任意のドットパターンを「文字」として表示でき、グラフィックスに近い表現が可能でした。しかしBBSに投稿するならPCGは使えません。相手の画面にPCGのデータは存在せず、表示できるのは標準のASCII文字だけだからです。 &lt;a href=&quot;https://codedchords.dev/blog/2026/02/img2char-ascii-art-1986/#fr-2-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
        <summary type="html">1986年、8ビットパソコンSharp X1で画像をASCII文字に自動変換し、
パソコン通信のBBSに投稿していました。当時のアルゴリズムを
GoでCLIツール img2char として再実装した経緯と技術を紹介します。
</summary>
        </entry><entry xml:lang="ja">
        <title>多機能Todoに疲れた人へ ─「一枚の紙」だけのタスク管理CLI</title>
        <published>2026-01-23T00:00:00+00:00</published>
        <updated>2026-01-23T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2026/01/building-ttt-with-claude-code/" type="text/html"/>
        <id>https://codedchords.dev/blog/2026/01/building-ttt-with-claude-code/</id>
        
            <content type="html"> &lt;details&gt;
  &lt;summary&gt;Table of Contents&lt;/summary&gt;
  &lt;!-- toc --&gt;
  &lt;/details&gt;
&lt;h2 id=&quot;はじめに&quot;&gt;はじめに&lt;/h2&gt;
&lt;p&gt;ターミナルで作業中、ふと「あれもやらなきゃ」と思いついたとき、頭の中を書き出す場所が欲しくなります。
物理的なメモ用紙ならすぐに書けますが、ターミナルから手を離すのは煩わしい。
かといって多機能なTodoアプリを開くと、プロジェクトやタグを考えているうちに書きたかったことを忘れてしまう。&lt;/p&gt;
&lt;p&gt;そこで作ったのが &lt;strong&gt;Tiny Task Tool&lt;/strong&gt; です。コマンド名はタイプしやすいように &lt;code&gt;ttt&lt;/code&gt; としました。&lt;/p&gt;
&lt;p&gt;「一枚の紙」のシンプルさをターミナルで再現することを目指した、ミニマルなタスク管理ツールです。&lt;/p&gt;
&lt;p&gt;この記事では、&lt;code&gt;ttt&lt;/code&gt; のコンセプトと使い方を紹介しつつ、Claude Codeとの協働開発でドキュメント駆動とTDDがいかに効果的だったかをお伝えします。&lt;/p&gt;
&lt;h2 id=&quot;Tiny_Task_Tool_とは&quot;&gt;Tiny Task Tool とは&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ttt&lt;/code&gt;(Tiny Task Tool) は「デジタルなメモ用紙」を目指したTUIタスク管理ツールです。&lt;/p&gt;
&lt;!-- textlint-disable --&gt;
&lt;img src=&quot;https://codedchords.dev/blog/2026/01/building-ttt-with-claude-code/demo.gif?h=5668a44b60ecfc9113e6&quot; alt=&quot;ttt demo&quot; width=&quot;800&quot; height=&quot;600&quot; loading=&quot;lazy&quot; /&gt;
&lt;!-- textlint-enable --&gt;
&lt;h3 id=&quot;主な特徴&quot;&gt;主な特徴&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;一枚の紙&lt;/strong&gt;: ファイルは常に1つ。「どのファイルに書こう」という選択を排除&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;li&gt;&lt;strong&gt;Unix 哲学&lt;/strong&gt;: フォーカス機能以外は外部エディタやGitに任せる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;インストール&quot;&gt;インストール&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# go install（推奨）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install github.com/yostos/tiny-task-tool@latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Homebrew&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install yostos/tap/ttt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;基本的な使い方&quot;&gt;基本的な使い方&lt;/h3&gt;
&lt;p&gt;ターミナルで &lt;code&gt;ttt&lt;/code&gt; と打つだけで、1ページのタスク表がTUIで開きます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ttt&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;                    # TUIでタスク表を開く&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ttt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -t &amp;quot;牛乳を買う&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;     # コマンドラインからタスクを追加&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ttt&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; sync&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;               # git pushでリモートと同期&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TUIでは &lt;code&gt;e&lt;/code&gt; でエディタを開き、&lt;code&gt;a&lt;/code&gt; でアーカイブ、&lt;code&gt;q&lt;/code&gt; で終了。キーバインドはVim/Emacsユーザー向けにカスタマイズ可能です。&lt;/p&gt;
&lt;h3 id=&quot;タスクのフォーマット&quot;&gt;タスクのフォーマット&lt;/h3&gt;
&lt;p&gt;シンプルなMarkdownチェックボックス形式です。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span&gt; [ ] 未完了のタスク&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-8&quot;&gt; [x]&lt;/span&gt;&lt;span&gt; 完了したタスク @done(2026-01-18)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;  -&lt;/span&gt;&lt;span class=&quot;z-8&quot;&gt; [x]&lt;/span&gt;&lt;span&gt; 子タスクも自動で完了 @done(2026-01-18)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;タスク以外の箇条書きもノードとして挿入できます。タスクをグルーピングするためにセクション（&lt;code&gt;# Task&lt;/code&gt; や &lt;code&gt;## Project A&lt;/code&gt;）なども扱えます。&lt;/p&gt;
&lt;p&gt;完了したタスクには自動で &lt;code&gt;@done(日付)&lt;/code&gt; タグが付き、設定した日数が経過するとarchive.mdに移動します。&lt;/p&gt;
&lt;h2 id=&quot;なぜ作ったのか&quot;&gt;なぜ作ったのか&lt;/h2&gt;
&lt;p&gt;きっかけはZennで見かけた「&lt;a rel=&quot;external&quot; href=&quot;https://zenn.dev/nekoninaritai/articles/941e55aed77609&quot;&gt;Markdown Paper という選択肢&lt;/a&gt;」という記事でした。&lt;a rel=&quot;external&quot; href=&quot;https://ephe.app/&quot;&gt;ephe.app&lt;/a&gt; の「一枚の紙」コンセプトをCLIに持ち込んだ &lt;a rel=&quot;external&quot; href=&quot;https://github.com/siki-712/aeph&quot;&gt;aeph&lt;/a&gt; の紹介記事です。&lt;/p&gt;
&lt;p&gt;コンセプトには共感しましたが、aephは複数ファイルに対応しており、「一枚の紙」とは少し違うと感じました。そこでaephのソースコードを参考にするのではなく、ゼロから自分のコンセプトで作ることにしました。&lt;/p&gt;
&lt;p&gt;既存のツールにも同様の問題がありました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Todo アプリ&lt;/strong&gt;: 多機能すぎる。プロジェクト、優先度、タグ…考えているうちに書きたいことを忘れる&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown エディタ&lt;/strong&gt;: ファイル名やフォルダを考える必要がある&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GUI アプリ&lt;/strong&gt;: ターミナルから離れるコンテキストスイッチが発生する&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;単一ファイル、選択肢なし、気を散らすものなし。机の上のメモ用紙のように、「開いて、書いて、終わったら消す」だけの体験が欲しかったのです。&lt;/p&gt;
&lt;h2 id=&quot;Claude_Code_との開発体験&quot;&gt;Claude Code との開発体験&lt;/h2&gt;
&lt;p&gt;このプロジェクトはClaude Codeを活用し、以下のプロセスで開発しました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;コンセプト文書化（concept.md）&lt;/li&gt;
&lt;li&gt;詳細仕様策定（specification.md）&lt;/li&gt;
&lt;li&gt;アーキテクチャ選定（architecture.md）&lt;/li&gt;
&lt;li&gt;テストコード先行開発&lt;/li&gt;
&lt;li&gt;実装&lt;/li&gt;
&lt;li&gt;テスト自動化&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;ドキュメント駆動が効いた理由&quot;&gt;ドキュメント駆動が効いた理由&lt;/h3&gt;
&lt;p&gt;Claude Codeとの協働で最も効果的だったのは、&lt;strong&gt;まずドキュメントを書き、それに従って開発する&lt;/strong&gt;アプローチでした。&lt;/p&gt;
&lt;h4 id=&quot;1._concept.md_で方向性を固める&quot;&gt;1. concept.md で方向性を固める&lt;/h4&gt;
&lt;p&gt;最初に「なぜ作るのか」「何をしないか」を明確にしました。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-10&quot;&gt;### やらないこと&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span&gt; ファイル指定（「どのファイルを開くか」という選択を排除）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span&gt; 複数ファイル管理（一枚の紙の原則に反する）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span&gt; ファイル内編集（外部エディタに任せる）&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;「やらないこと」を明文化したことで、開発中に機能追加の誘惑が来ても「concept.mdに反する」と判断できました。Claudeにも「この機能はconcept.mdの方針に反しますね」とフィードバックを受けられます。&lt;/p&gt;
&lt;h4 id=&quot;2._specification.md_で仕様を詳細化&quot;&gt;2. specification.md で仕様を詳細化&lt;/h4&gt;
&lt;p&gt;ユースケース、画面レイアウト、エラー処理まで事前に定義しました。これによりClaudeへの指示が明確になり、生成されるコードの品質が上がりました。&lt;/p&gt;
&lt;h4 id=&quot;3._architecture.md_で技術選定を記録&quot;&gt;3. architecture.md で技術選定を記録&lt;/h4&gt;
&lt;p&gt;「なぜその技術を選んだか」「他の選択肢をなぜ却下したか」を記録しました。後から見返したとき、またClaudeに文脈を伝えるときに役立ちました。基本的に記載方法は &lt;strong&gt;Architectural Decision Record（ADR）形式&lt;/strong&gt; を採用して記載しています。&lt;/p&gt;
&lt;p&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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Rust&lt;/td&gt;&lt;td&gt;学習コストが高い。所有権/ライフタイムの概念が独特で、AI 駆動開発でコンパイルエラー修正時に堂々巡りするリスク&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Python&lt;/td&gt;&lt;td&gt;PEP 668 により配布が複雑化。起動速度も遅い&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;TypeScript&lt;/td&gt;&lt;td&gt;単一バイナリ配布ができず「摩擦をなくす」原則に反する&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;TDD_が_AI_駆動開発と相性が良い理由&quot;&gt;TDD が AI 駆動開発と相性が良い理由&lt;/h3&gt;
&lt;p&gt;テスト駆動開発（TDD）はClaude Codeとの開発で特に威力を発揮しました。&lt;/p&gt;
&lt;h4 id=&quot;テストファーストのメリット&quot;&gt;テストファーストのメリット&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;期待動作の明確化&lt;/strong&gt;: テストコードが「仕様書」として機能し、開発前に私が確認できるためClaudeへの指示が曖昧にならない&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;リグレッション防止&lt;/strong&gt;: AIが生成したコードの変更が既存機能を壊していないか即座に確認&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;リファクタリングの安心感&lt;/strong&gt;: テストが通れば安心して改善できる&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;実際の開発フロー&quot;&gt;実際の開発フロー&lt;/h4&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 1. まずテストを書く（Red）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; go test ./...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# FAIL: TestArchiveCompletedTasks - 期待する動作のテストが失敗&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 2. Claude に実装を依頼&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 「このテストを通す実装をお願いします」&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 3. テストが通ることを確認（Green）&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; go test ./...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# PASS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 4. リファクタリング&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# 「この実装をより読みやすくリファクタリングしてください」&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特に階層タスクのアーカイブ処理のような複雑なロジックでは、テストケースを先に書いておくことで、エッジケースの考慮漏れを防げました。&lt;/p&gt;
&lt;h3 id=&quot;Claude_Code_との対話で心がけたこと&quot;&gt;Claude Code との対話で心がけたこと&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;ドキュメントを参照させる&lt;/strong&gt;:「concept.mdの方針に従って」「specification.mdの仕様通りに」&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;h2 id=&quot;技術スタック&quot;&gt;技術スタック&lt;/h2&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;Go&lt;/td&gt;&lt;td&gt;単一バイナリ配布、高速起動&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;TUI&lt;/td&gt;&lt;td&gt;bubbletea + lipgloss&lt;/td&gt;&lt;td&gt;成熟したエコシステム、Elm アーキテクチャ&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;設定&lt;/td&gt;&lt;td&gt;pelletier/go-toml v2&lt;/td&gt;&lt;td&gt;TOML 1.0 準拠、高速&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;テスト&lt;/td&gt;&lt;td&gt;testing + testify&lt;/td&gt;&lt;td&gt;標準+簡潔なアサーション&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ttt&lt;/code&gt; は「一枚の紙」という思想にこだわった結果、機能を削ることで価値を磨けたツールになったと思います。私自身も便利に使っています。もっと高機能なツールはたくさん
ありますが、コンセプトに賛同いただける多くの方に使ってもらえれば幸いです。&lt;/p&gt;
&lt;p&gt;Claude Codeとの開発では、&lt;strong&gt;ドキュメント駆動&lt;/strong&gt; と &lt;strong&gt;TDD&lt;/strong&gt; の組み合わせが非常に効果的でした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ドキュメント駆動&lt;/strong&gt;: 要件だけでなく方針を文書化するところから、Claude Codeと協働する&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TDD&lt;/strong&gt;: 期待動作を定義し、品質を担保する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「Claude Codeに任せきり」ではなく「Claude Codeと協働する」開発スタイルとして、このアプローチはおすすめできます。&lt;/p&gt;
&lt;p&gt;興味を持っていただけたら、ぜひ使ってみてください。&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/yostos/tiny-task-tool&quot;&gt;https://github.com/yostos/tiny-task-tool&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;この記事で紹介したドキュメント（concept.md, specification.md, architecture.md）は GitHub リポジトリの docs/ ディレクトリで公開しています。&lt;/em&gt;&lt;/p&gt;
</content>
        <summary type="html">多機能Todoアプリに疲れた方へ。「一枚の紙」だけのシンプルさを
再現したミニマルなタスク管理CLI「Tiny Task Tool (ttt)」を
開発しました。Claude Codeとの協働開発で実感した、
ドキュメント駆動とTDDの効果的な組み合わせをご紹介します。
</summary>
        </entry><entry xml:lang="ja">
        <title>Logic ProのAIFファイルをラウドネス正規化してFLACに変換するスクリプト</title>
        <published>2026-01-14T00:00:00+00:00</published>
        <updated>2026-10-06T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2026/01/aif2flac/" type="text/html"/>
        <id>https://codedchords.dev/blog/2026/01/aif2flac/</id>
        
            <content type="html">&lt;!-- textlint-disable --&gt;
&lt;div class=&quot;admonition info&quot;&gt;
&lt;div class=&quot;admonition-icon admonition-icon-info&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;admonition-content&quot;&gt;
&lt;strong class=&quot;admonition-title&quot;&gt;INFO&lt;/strong&gt;
&lt;p&gt;この記事の公開後、Waves L4 Ultramaximizerでマスタリングするようになり、現在はこのスクリプトを使っていません。理由と、それでもこのスクリプトが役に立つ場面を末尾の追記にまとめました。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;!-- textlint-enable --&gt;
&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;
&lt;p&gt;Logic Proでバウンスした音声ファイル(Aiff)をFLACに変換する際、適切な音量に正規化する必要があります。ffmpegの&lt;code&gt;loudnorm&lt;/code&gt;フィルタを使うと高品質なラウドネス正規化が可能ですが、最良の結果を得るには2パス処理が推奨されます。&lt;/p&gt;
&lt;p&gt;この2パス処理は以下のような手順です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1回目の実行で音声のラウドネス値を測定&lt;/li&gt;
&lt;li&gt;測定された値を確認し、手動で2回目のコマンドにコピー&lt;/li&gt;
&lt;li&gt;2回目の実行で測定値を使用して正規化を適用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;毎回この作業を手動で行うのは面倒なので、自動化するスクリプトを作成しました。&lt;/p&gt;
&lt;h2 id=&quot;スクリプトの内容&quot;&gt;スクリプトの内容&lt;/h2&gt;
&lt;p&gt;以下は公開後に改訂したコードです。改訂の内容は末尾の追記にまとめました。&lt;a rel=&quot;external&quot; href=&quot;https://gist.github.com/yostos/004e57e4a905f933e227aa183e3a8fec&quot;&gt;Gist&lt;/a&gt;でも公開しています。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot; data-name=&quot;aif2flac.sh&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-16 z-i&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Script to convert AIF files to FLAC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# By default, performs pure format conversion without any audio processing,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# then verifies that the FLAC decodes back to bit-identical PCM.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Use -n/--normalize to apply EBU R128 loudness normalization; that mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# refuses to run unless it can be done as a pure gain change (linear mode).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Copyright (c) 2026 yostos&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Licensed under the MIT License&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# https://opensource.org/licenses/MIT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;VERBOSE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;NORMALIZE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;VERIFY&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ALLOW_DYNAMIC&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Loudness targets. Used for BOTH the measurement pass and the encoding pass;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# they must match, otherwise loudnorm&amp;#39;s target_offset is computed against a&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# different target than the one actually applied.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;TARGET_I&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;-14&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;TARGET_TP&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;TARGET_LRA&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Usage: &lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; [-v|--verbose] [-n|--normalize] [--no-verify] [--allow-dynamic] &amp;lt;input_file.aif&amp;gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Parse options&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [[&lt;/span&gt;&lt;span&gt; $#&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -gt&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ]];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    case&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; $1&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -v&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;--verbose&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            VERBOSE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            shift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -n&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;--normalize&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            NORMALIZE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            shift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        --no-verify&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            VERIFY&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            shift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        --allow-dynamic&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ALLOW_DYNAMIC&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            shift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -h&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;--help&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;            usage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Options:&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  -v, --verbose      Show detailed ffmpeg output&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  -n, --normalize    Apply EBU R128 loudness normalization (two-pass)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;      --no-verify    Skip the bit-exactness check in conversion mode&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;      --allow-dynamic  Permit loudnorm&amp;#39;s dynamic mode (alters dynamics)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  -h, --help         Show this help message&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;By default, performs pure format conversion (AIF -&amp;gt; FLAC) without&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;any audio processing, and verifies the result is bit-identical to&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;the source. Use -n to apply loudness normalization.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Normalization is only performed when loudnorm can run in linear&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;mode (a single constant gain). If the requested target cannot be&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;reached without compression/limiting, the script aborts instead of&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;silently degrading the audio. Use --allow-dynamic to override.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: Unknown option &lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;            usage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-1&quot;&gt;        *)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            shift&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            ;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    esac&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    usage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Use -h or --help for more information&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ! -f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: File &amp;#39;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39; not found&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Generate output filename (change extension to .flac)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;${INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;*&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;.flac&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Run ffmpeg, capturing output so errors can be reported.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# NOTE: `set -e` aborts the shell on a failing command substitution before the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# next line runs, so $? must be captured with `set +e` around the assignment.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;FFMPEG_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;run_ffmpeg&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$VERBOSE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # Tee to a temp file so the output is still available for the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # post-encode checks below while being shown live.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        local&lt;/span&gt;&lt;span&gt; tmplog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        tmplog&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;mktemp&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; +e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;        ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; 2&amp;gt;&amp;amp;1 |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; tee&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$tmplog&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        FFMPEG_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;${PIPESTATUS&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;cat&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$tmplog&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;        rm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -f &amp;quot;&lt;/span&gt;&lt;span&gt;$tmplog&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; +e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -hide_banner &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; 2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        FFMPEG_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;$?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span&gt; $FFMPEG_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -ne&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error during encoding (exit code: &lt;/span&gt;&lt;span&gt;$FFMPEG_EXIT_CODE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -n&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ] &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Decode both files to a common 32-bit format and compare hashes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# `-f md5` alone defaults to pcm_s16le, which would compare only the top 16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# bits and report a false match, so the codec must be pinned explicitly.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;verify_lossless&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    local&lt;/span&gt;&lt;span&gt; src&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; dst&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; a b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;=== Verifying bit-exactness... ===&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; +e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    a&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v error -i &amp;quot;&lt;/span&gt;&lt;span&gt;$src&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -c:a pcm_s32le -f md5 -&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    local&lt;/span&gt;&lt;span&gt; rc_a&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;$?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    b&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v error -i &amp;quot;&lt;/span&gt;&lt;span&gt;$dst&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -c:a pcm_s32le -f md5 -&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    local&lt;/span&gt;&lt;span&gt; rc_b&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;$?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    set&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -e&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span&gt; $rc_a&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -ne&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ]&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ||&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span&gt; $rc_b&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -ne&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: verification failed to decode one of the files&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$a&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$b&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  OK: output is bit-identical to the source (&lt;/span&gt;&lt;span&gt;${a&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;#&lt;/span&gt;&lt;span&gt;MD5&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;=&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  FAILED: output does NOT match the source&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;    source: &lt;/span&gt;&lt;span&gt;${a&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;#&lt;/span&gt;&lt;span&gt;MD5&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;=&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;    output: &lt;/span&gt;&lt;span&gt;${b&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;#&lt;/span&gt;&lt;span&gt;MD5&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;=&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  The FLAC encoder supports 16- and 24-bit only; a 32-bit float&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  source cannot be stored losslessly and will fail this check.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$NORMALIZE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # === Loudness normalization mode (two-pass) ===&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Detect input sample rate so it is preserved through the loudnorm filter.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # loudnorm operates internally at 192kHz in dynamic mode and would&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # otherwise force the output to 192kHz, so pin the output rate explicitly.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    INPUT_SR&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffprobe&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v error -select_streams a:0&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -show_entries stream=sample_rate -of csv=p=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_SR&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: Could not determine input sample rate&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;=== Step 1: Measuring loudness... ===&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Measure with the same targets used for encoding.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -i &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -af &amp;quot;loudnorm=I=&lt;/span&gt;&lt;span&gt;$TARGET_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:TP=&lt;/span&gt;&lt;span&gt;$TARGET_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:LRA=&lt;/span&gt;&lt;span&gt;$TARGET_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:print_format=json&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -f null -&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; 2&amp;gt;&amp;amp;1 |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -A&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 12&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Parsed_loudnorm&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Extract required values from JSON&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MEASURED_I&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;quot;input_i&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -F&amp;#39;&amp;quot;&amp;#39; &amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MEASURED_TP&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;quot;input_tp&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -F&amp;#39;&amp;quot;&amp;#39; &amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;quot;input_lra&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -F&amp;#39;&amp;quot;&amp;#39; &amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;quot;input_thresh&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -F&amp;#39;&amp;quot;&amp;#39; &amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    TARGET_OFFSET&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LOUDNESS_JSON&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;&amp;quot;target_offset&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -F&amp;#39;&amp;quot;&amp;#39; &amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ]&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ||&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ]&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ||&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;       [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ]&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ||&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -z&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: Could not parse loudnorm measurement output&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Measurement results:&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Input Integrated: &lt;/span&gt;&lt;span&gt;$MEASURED_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LUFS&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Input True Peak:  &lt;/span&gt;&lt;span&gt;$MEASURED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; dBTP&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Input LRA:        &lt;/span&gt;&lt;span&gt;$MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LU&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Input Threshold:  &lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LUFS&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Target Offset:    &lt;/span&gt;&lt;span&gt;$TARGET_OFFSET&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LU&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Predict whether loudnorm can stay in linear mode (constant gain, no&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # compression, no limiting, no 192kHz resample round-trip). It falls back&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # to dynamic mode when the gain would push the true peak past the ceiling,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # when the threshold is unmeasurable, or when measured_LRA is exactly 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # (loudnorm reads 0 as &amp;quot;not supplied&amp;quot; and disables linear mode).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    LINEAR_CHECK&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v i=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -v tp=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;                       -v lra=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -v th=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;                       -v ti=&amp;quot;&lt;/span&gt;&lt;span&gt;$TARGET_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -v ttp=&amp;quot;&lt;/span&gt;&lt;span&gt;$TARGET_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; &amp;#39;BEGIN {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        offset_tp = tp + (ti - i);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        max_i     = ti - (offset_tp - ttp);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        ok = (lra &amp;gt; 0) &amp;amp;&amp;amp; (th &amp;gt; -70) &amp;amp;&amp;amp; (offset_tp &amp;lt;= ttp);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        printf &amp;quot;%d %.2f %.2f&amp;quot;, ok, offset_tp, max_i;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    }&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    LINEAR_OK&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LINEAR_CHECK&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    PREDICTED_TP&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LINEAR_CHECK&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MAX_LINEAR_I&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LINEAR_CHECK&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;{print $3}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LINEAR_OK&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ] &amp;amp;&amp;amp; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$ALLOW_DYNAMIC&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: this file cannot be normalized to &lt;/span&gt;&lt;span&gt;$TARGET_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LUFS by gain alone.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v l=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; &amp;#39;BEGIN {exit !(l &amp;lt;= 0)}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Measured LRA is 0.00 LU, which loudnorm treats as unset and&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  which forces its dynamic (compressing) mode.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        elif&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v t=&amp;quot;&lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; &amp;#39;BEGIN {exit !(t &amp;lt;= -70)}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Measured threshold (&lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LUFS) is too low to measure reliably.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  The required gain would raise true peak to &lt;/span&gt;&lt;span&gt;$PREDICTED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; dBTP,&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  above the &lt;/span&gt;&lt;span&gt;$TARGET_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; dBTP ceiling, so loudnorm would silently switch&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  to its dynamic mode and apply compression and limiting.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  The loudest linear-safe target for this file is &lt;/span&gt;&lt;span&gt;$MAX_LINEAR_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; LUFS.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Aborting to leave the master untouched.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  Convert without -n, or pass --allow-dynamic to override.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$LINEAR_OK&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Linear (gain-only) normalization is possible; predicted output true peak: &lt;/span&gt;&lt;span&gt;$PREDICTED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; dBTP&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;WARNING: --allow-dynamic given; loudnorm will compress and limit this file.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;=== Step 2: Encoding to FLAC with normalization... ===&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Encode to FLAC using measured values.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Note: in linear mode loudnorm ignores `offset` and recomputes it from the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # measured values; it is passed for the --allow-dynamic path.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    run_ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -y -i &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -af &amp;quot;loudnorm=I=&lt;/span&gt;&lt;span&gt;$TARGET_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:TP=&lt;/span&gt;&lt;span&gt;$TARGET_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:LRA=&lt;/span&gt;&lt;span&gt;$TARGET_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:measured_I=&lt;/span&gt;&lt;span&gt;$MEASURED_I&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:measured_TP=&lt;/span&gt;&lt;span&gt;$MEASURED_TP&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:measured_LRA=&lt;/span&gt;&lt;span&gt;$MEASURED_LRA&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:measured_thresh=&lt;/span&gt;&lt;span&gt;$MEASURED_THRESH&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:offset=&lt;/span&gt;&lt;span&gt;$TARGET_OFFSET&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;:linear=true:print_format=summary&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        -ar &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_SR&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -c:a flac -compression_level&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$VERBOSE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -A&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 15&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Input Integrated:&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v &amp;quot;^\[&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v &amp;quot;^$&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ||&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Confirm loudnorm actually stayed linear; it downgrades silently.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$FFMPEG_OUTPUT&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -q &amp;quot;Normalization Type:[[:space:]]*Dynamic&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$ALLOW_DYNAMIC&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Error: loudnorm fell back to dynamic mode despite the pre-flight check.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;  The output has been compressed and limited; removing it.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;            rm&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -f &amp;quot;&lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;            exit&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;WARNING: dynamic mode was used; dynamics have been altered.&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;else&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # === Pure conversion mode (default) ===&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;=== Converting to FLAC... ===&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # No filters and no -sample_fmt: ffmpeg carries 16-bit through as 16-bit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # and 24-bit through as 24-bit. -compression_level only trades encode time&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # for file size; the decoded samples are identical at every level.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    run_ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -y -i &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; -c:a flac -compression_level&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$VERIFY&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; ];&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;        echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;        verify_lossless&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;=== Completed ===&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Output file: &lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Display file sizes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; command&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -v du&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; /dev/null&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    INPUT_SIZE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;du&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -h &amp;quot;&lt;/span&gt;&lt;span&gt;$INPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    OUTPUT_SIZE&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;du&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -h &amp;quot;&lt;/span&gt;&lt;span&gt;$OUTPUT_FILE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Input file:  &lt;/span&gt;&lt;span&gt;$INPUT_SIZE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;    echo&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;Output file: &lt;/span&gt;&lt;span&gt;$OUTPUT_SIZE&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;使い方&quot;&gt;使い方&lt;/h2&gt;
&lt;p&gt;AIFファイルのパスを引数に指定して実行します。オプションを付けなければ、ラウドネスには手を加えずFLACに変換し、変換後の音声が元のファイルとビット単位で一致するかを検証します。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./aif2flac.sh&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.aif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ラウドネス正規化をする場合は&lt;code&gt;-n&lt;/code&gt;オプションを付けます。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./aif2flac.sh&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -n input.aif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ほかのオプションは次のとおりです。出力ファイルは同じディレクトリに&lt;code&gt;.flac&lt;/code&gt;拡張子で保存されます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-v&lt;/code&gt;: ffmpegの詳細な出力を表示する&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--no-verify&lt;/code&gt;: 変換後の一致検証を省く&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--allow-dynamic&lt;/code&gt;: &lt;code&gt;loudnorm&lt;/code&gt;のダイナミックモードでの処理を許可する（音のダイナミクスが変わる）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;loudnormフィルタについて&quot;&gt;loudnormフィルタについて&lt;/h2&gt;
&lt;p&gt;ffmpegの&lt;code&gt;loudnorm&lt;/code&gt;フィルタは、EBU R128規格に基づいたラウドネス正規化を行うためのフィルタです。単純な音量の増減ではなく、人間の聴覚特性を考慮した知覚的な音量の均一化を実現します。&lt;/p&gt;
&lt;p&gt;このスクリプトでは以下のパラメータを使用しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;I=-14&lt;/code&gt;: 目標のIntegrated Loudness（LUFS単位）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TP=-1&lt;/code&gt;: True Peak値の上限（dBTP単位）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LRA=11&lt;/code&gt;: Loudness Range（LU単位）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;I=-14 LUFS&lt;/code&gt;は、Spotify、YouTube Music、Apple Musicなどの主要なストリーミングサービスが採用している標準的なラウドネス基準です。世間一般で広く使われているこの基準を参考にすることで、適切な音量バランスを保つことができます。&lt;/p&gt;
&lt;p&gt;1パス処理では入力音声の特性を考慮せずに正規化するため、音質劣化の可能性があります。2パス処理では、1回目で測定した入力音声の実際の値を2回目の処理に使用することで、より正確で高品質な正規化が可能になります。&lt;/p&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;Logic Proから書き出した音声ファイルをFLACに変換する際の2パス処理を自動化することで、手動でパラメータをコピーする手間がなくなり、作業効率が大幅に向上しました。&lt;/p&gt;
&lt;p&gt;今後は、複数ファイルの一括処理や、ターゲットラウドネス値のカスタマイズなどの機能を追加するかもしれません。&lt;/p&gt;
&lt;h2 id=&quot;追記:_L4_Ultramaximizer導入後にこのスクリプトをやめた理由と残る価値&quot;&gt;追記: L4 Ultramaximizer導入後にこのスクリプトをやめた理由と残る価値&lt;/h2&gt;
&lt;p&gt;この記事を書いた後、Logic ProのマスターにWaves L4 Ultramaximizerを入れ、ラウドネスとLRAをマスタリングの段階で作り込むようになりました。そうなると、このスクリプトがやっていることは「最終的に音量を-14 LUFSに合わせる一律のゲイン調整」だけになり、LRAの調整としてはほぼ意味がありません。場合によっては、マスタリングの結果を壊すおそれもあります。&lt;/p&gt;
&lt;p&gt;理由は&lt;code&gt;loudnorm&lt;/code&gt;の動作にあります。2パスで&lt;code&gt;linear=true&lt;/code&gt;を指定した場合、次の2つの条件を両方満たすときだけ、単純な一律ゲイン変更（線形モード）になります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入力のLRAが指定値（&lt;code&gt;LRA=11&lt;/code&gt;）以下である&lt;/li&gt;
&lt;li&gt;ゲインを変えた後のTrue Peakが-1 dBTP以下に収まる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L4で仕上げた音源ならLRAはたいてい11 LU以下なので、&lt;code&gt;LRA=11&lt;/code&gt;は条件の判定に使われるだけで、LRAそのものは変わりません。一方で条件を満たさない場合、&lt;code&gt;loudnorm&lt;/code&gt;は何も言わずにダイナミックモードへ切り替わり、独自のコンプレッションとリミッティングをかけます。これはL4での作業と二重処理になります。&lt;/p&gt;
&lt;p&gt;そのため現在は、ラウドネスには手を加えず、形式だけを変換しています。FLACは可逆圧縮なので、L4で仕上げた結果がそのまま残ります。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ffmpeg&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -i xxxx.aiff -c:a flac -compression_level&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 8&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; xxxx.flac&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;マスタリング済みの音源に使う理由はなくなりましたが、このスクリプトの価値がまったくなくなったわけではありません。次のような場面では今でも役に立ちます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;リミッターを通していないバウンスやラフミックスを、手早く-14 LUFS前後にそろえたいとき&lt;/li&gt;
&lt;li&gt;形式変換の結果が元の音声とビット単位で一致するかを確かめたいとき&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loudnorm&lt;/code&gt;の2パス処理を自動化する方法の例として参照したいとき&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なお、スクリプトは公開後に次のように改訂しています。記事中のコードは改訂後のものです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;既定ではラウドネス正規化をせず、形式だけを変換する。変換後は、元のファイルとFLACをそれぞれデコードしたPCMのハッシュを比べ、ビット単位で一致するかを検証する&lt;/li&gt;
&lt;li&gt;正規化は&lt;code&gt;-n&lt;/code&gt;を付けたときだけ行う。測定値から線形モードで処理できないと見込んだ場合は、出力を作らずに中止する&lt;/li&gt;
&lt;li&gt;正規化の後、サマリーの&lt;code&gt;Normalization Type&lt;/code&gt;が&lt;code&gt;Dynamic&lt;/code&gt;になっていれば、出力ファイルを削除してエラーで終了する&lt;/li&gt;
&lt;li&gt;出力のサンプルレートを&lt;code&gt;-ar&lt;/code&gt;で入力と同じ値に固定する。ダイナミックモードでは出力が192 kHzにアップサンプリングされるため&lt;/li&gt;
&lt;li&gt;FLACの圧縮レベルを最大の8にする。圧縮レベルで変わるのはエンコード時間とファイルサイズだけで、デコードした音声は同じになる&lt;/li&gt;
&lt;/ul&gt;
</content>
        <summary type="html">Logic Proでバウンスした音声ファイルをFLACに変換する際、
ffmpegのloudnormフィルタによる2パス処理を自動化する
Bashスクリプトを作成しました。測定と適用を一度のコマンドで
完結させ、手作業を排除して作業効率を大幅に向上させます。
</summary>
        </entry><entry xml:lang="ja">
        <title>GitHub Actionsでnpmパッケージ公開を自動化する - Trusted Publishingで実現するトークンレス運用</title>
        <published>2025-12-05T00:00:00+00:00</published>
        <updated>2025-12-05T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2025/12/automating-npm-publish-with-github-actions/" type="text/html"/>
        <id>https://codedchords.dev/blog/2025/12/automating-npm-publish-with-github-actions/</id>
        
            <content type="html">&lt;p&gt;本記事では、私が開発している&lt;a rel=&quot;external&quot; href=&quot;https://github.com/yostos/jrnl-mcp&quot;&gt;jrnl-mcp&lt;/a&gt;パッケージで実装した、
GitHubが発表した&lt;a rel=&quot;external&quot; href=&quot;https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/&quot;&gt;npmの認証強化&lt;/a&gt;
対応の
Trusted Publishing (OIDC) を使用したトークン管理不要の自動公開について記載しています。&lt;/p&gt;
&lt;h2 id=&quot;npmの認証変更の背景&quot;&gt;npmの認証変更の背景&lt;/h2&gt;
&lt;p&gt;GitHubは2025年9月29日に、npmのセキュリティ強化として以下の変更を発表しました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classic Access Tokenの廃止&lt;/li&gt;
&lt;li&gt;Trusted Publishing (OIDC) またはGranular Access Tokenへの移行推奨&lt;/li&gt;
&lt;li&gt;より細かい権限管理の実現&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;移行先として2つの選択肢があります。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Granular Access Token&lt;/li&gt;
&lt;li&gt;Trusted Publishing (OIDC)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Granular Access Tokenは、従来のトークン方式を改良したものでパッケージごとの権限管理が可能となっていますが、定期的なトークンのローテーションが必要です。
Trusted Publishingはトークンを完全に排除した新しい認証方式で、公開を自動化しトークンを完全に排除したものです。&lt;/p&gt;
&lt;p&gt;jrnl-mcpでは、トークン管理の手間を完全に排除できる &lt;strong&gt;Trusted Publishing&lt;/strong&gt; を採用しました。&lt;/p&gt;
&lt;h2 id=&quot;自動公開の仕組み&quot;&gt;自動公開の仕組み&lt;/h2&gt;
&lt;h3 id=&quot;全体のフロー&quot;&gt;全体のフロー&lt;/h3&gt;
&lt;p&gt;jrnl-mcpでは、以下のフローで自動公開を実現しています。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GitHubでリリースを作成&lt;/li&gt;
&lt;li&gt;GitHub Actionsが自動起動&lt;/li&gt;
&lt;li&gt;依存関係のインストールとテストを実行&lt;/li&gt;
&lt;li&gt;npmへ自動公開&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この仕組みにより、リリース作成だけでパッケージの公開が完了します。&lt;/p&gt;
&lt;h3 id=&quot;GitHub_Actionsの設定&quot;&gt;GitHub Actionsの設定&lt;/h3&gt;
&lt;p&gt;実際に使用しているワークフロー設定は以下の通りです。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Publish to npm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;  release&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    types&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;published&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;  contents&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; read&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;  id-token&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; write&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;  publish&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    runs-on&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    steps&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      -&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Checkout code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        uses&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; actions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      -&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Setup Node.js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        uses&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        with&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;          node-version&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;20&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;          registry-url&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;https://registry.npmjs.org&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      -&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Install dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        run&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; npm ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      -&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Run tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        run&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; npm run test:ci&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;      -&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; name&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Publish to npm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;        run&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; npm publish --provenance --access public&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重要なポイントは、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;GitHubのリリース機能を使用することで、手動でのトリガーが可能になる。これにより、意図しないタイミングでの公開を防ぐ。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;permissions&lt;/code&gt;の設定により、GitHub Actionsはnpmに対して一時的なOIDCトークンを発行・認証する。長期的なトークンをSecretに保存する必要がない。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm publish --provenance --access public&lt;/code&gt;により自動的にprovenance（出所証明）が生成され、パッケージがどのリポジトリから公開されたか検証可能となる。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm run test:ci&lt;/code&gt;により公開前のテストを自動化する。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Trusted_Publishing_の設定方法&quot;&gt;Trusted Publishing の設定方法&lt;/h2&gt;
&lt;p&gt;Trusted Publishingを使用する場合、npm側での設定が必要です。&lt;/p&gt;
&lt;h3 id=&quot;1._npm_でパッケージを公開&quot;&gt;1. npm でパッケージを公開&lt;/h3&gt;
&lt;p&gt;まず、通常の方法で初回のパッケージを公開します（トークンを使用）。npm側でパッケージが存在している必要があるためです。&lt;/p&gt;
&lt;h3 id=&quot;2._npm_で_Trusted_Publishing_を設定&quot;&gt;2. npm で Trusted Publishing を設定&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.npmjs.com/&quot;&gt;npm のパッケージ設定&lt;/a&gt;にアクセス&lt;/li&gt;
&lt;li&gt;公開したパッケージの&quot;Settings&quot;タブを開く&lt;/li&gt;
&lt;li&gt;&quot;Publishing access&quot;セクションで&quot;Add a publisher&quot;を選択&lt;/li&gt;
&lt;li&gt;GitHub Actionsを選択し、以下を設定する
&lt;ul&gt;
&lt;li&gt;Repository: &lt;code&gt;&amp;lt;username&amp;gt;/&amp;lt;repository-name&amp;gt;&lt;/code&gt;（例: &lt;code&gt;yostos/jrnl-mcp&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Workflow: ワークフローファイル名（例: &lt;code&gt;publish.yml&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Environment: 未指定（デフォルト）または特定の環境名&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;3._GitHub_Actions_の設定&quot;&gt;3. GitHub Actions の設定&lt;/h3&gt;
&lt;p&gt;前述のワークフロー設定で、&lt;code&gt;id-token: write&lt;/code&gt; 権限を付与するだけで完了です。トークンをSecretに保存する必要はありません。&lt;/p&gt;
&lt;h3 id=&quot;トークンベースとの比較&quot;&gt;トークンベースとの比較&lt;/h3&gt;
&lt;p&gt;Trusted Publishingを使用することで、以下の作業が不要になります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npmトークンの作成と保存&lt;/li&gt;
&lt;li&gt;GitHub Secretsへのトークン登録&lt;/li&gt;
&lt;li&gt;トークンの定期的なローテーション（Granular Access Tokenの場合、最大90日ごと）&lt;/li&gt;
&lt;li&gt;トークン漏洩のリスク管理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;Trusted Publishing (OIDC) とGitHub Actionsを使用したnpmパッケージの自動公開により、以下のメリットが得られました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公開作業の効率化&lt;/li&gt;
&lt;li&gt;トークン管理の完全な排除&lt;/li&gt;
&lt;li&gt;セキュリティの向上&lt;/li&gt;
&lt;li&gt;品質の担保&lt;/li&gt;
&lt;li&gt;透明性の確保（自動provenance生成）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特に、Trusted Publishingの導入により、トークンの定期的なローテーションや漏洩リスクの心配が不要になり、セキュリティと運用性が大幅に向上しました。npmパッケージを公開している方は、ぜひTrusted Publishingへの移行とGitHub Actionsによる自動化を検討してみてください。&lt;/p&gt;
&lt;p&gt;Granular Access Tokenでもセキュリティは向上しますが、定期的なローテーション（最大90日ごと）が必要なため、可能であればTrusted Publishingの使用を推奨します。&lt;/p&gt;
&lt;h2 id=&quot;参考リンク&quot;&gt;参考リンク&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/&quot;&gt;Strengthening npm security: Important changes to authentication and token management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/yostos/jrnl-mcp&quot;&gt;jrnl-mcp GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://docs.npmjs.com/about-access-tokens&quot;&gt;npm Documentation: About access tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        <summary type="html">npm パッケージの公開を手動で行っていると、毎回トークンの管理やビルド、テストの実行など煩雑な作業が発生します。また、2025 年 9 月 29 日に GitHub が発表したnpmの認証強化により、Classic Token の廃止と、Trusted Publishing または Granular Access Token への移行が必要になりました。Trusted Publishing (OIDC) を使ってトークン管理不要のセキュアな公開フローを構築した手順を解説します。</summary>
        </entry><entry xml:lang="ja">
        <title>Apple Silicon時代のMacバックアップ最新戦略</title>
        <published>2025-04-18T00:00:00+00:00</published>
        <updated>2025-04-18T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2025/04/backup-strategy-for-mac/" type="text/html"/>
        <id>https://codedchords.dev/blog/2025/04/backup-strategy-for-mac/</id>
        
            <content type="html">&lt;h2 id=&quot;Macのバックアップ戦略&quot;&gt;Macのバックアップ戦略&lt;/h2&gt;
&lt;p&gt;macOSはOS自体が&lt;strong&gt;TimeMachine&lt;/strong&gt;という自動バックアップ機能を持っています。
以下のような特徴を持っています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;特徴&lt;/strong&gt;: macOSに標準搭載、独自形式でデータを保存&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;メリット&lt;/strong&gt;: 設定が簡単、自動バックアップ、過去バージョンへのアクセス&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;デメリット&lt;/strong&gt;: 独自形式のため通常のFinderからのアクセスが困難&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;アプローチ&lt;/strong&gt;: 問題発生時はリカバリーからOSインストール後、Migration Assistantでデータ復元&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;iMacを使っていた時は使っていましたが、Macbookを使うようになってから
TimeMachineの使用を止めました。
理由は、バックアップ用にディスクが常にマウントされているとは限らないので
「自動バックアップ」の恩恵が相対的に薄く、加えて独自形式のためTimeMachineの
メカニズムでしかバップアップにアクセスできないためです。&lt;/p&gt;
&lt;h2 id=&quot;Apple_Silicon時代のバックアップの変化&quot;&gt;Apple Silicon時代のバックアップの変化&lt;/h2&gt;
&lt;p&gt;TimeMachineを使用しない替わりに、長らく&lt;a rel=&quot;external&quot; href=&quot;https://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html&quot;&gt;SuperDuper&lt;/a&gt;
や&lt;a rel=&quot;external&quot; href=&quot;https://bombich.com/&quot;&gt;Carbon Copy Cloner&lt;/a&gt;を使いディスクごと
完全なクローンを取っていました。
完全なクローンはブート可能なので、リカバリーの際にはクローンディスクから
macOSをブートしてリカバリーを行うことができました。&lt;/p&gt;
&lt;p&gt;しかし、Apple Silicon搭載のMac（M1/M2/M3チップ）ではIntelが使われていた時代と
比較してシステムアーキテクチャが大きく変わりました。&lt;/p&gt;
&lt;p&gt;主な変更点は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OSが読み取り専用として保護されるようになった&lt;/li&gt;
&lt;li&gt;リカバリー機能がより強化された（Recovery・Fallback Recoveryが内蔵）&lt;/li&gt;
&lt;li&gt;外部ドライブからの起動に制限が追加された&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;バックアップ戦略も見直す必要が出てきました。
このためBootableな完全ディスククローンという戦略は以前ほど重要ではなくなりました。&lt;/p&gt;
&lt;h2 id=&quot;推奨バックアップ戦略&quot;&gt;推奨バックアップ戦略&lt;/h2&gt;
&lt;p&gt;次のようなバックアップ戦略を変更することが必要です。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;データ中心のバックアップ&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OSではなく、ユーザーデータを確実に保護する&lt;/li&gt;
&lt;li&gt;SuperDuperの場合は&lt;code&gt;Backup - User files&lt;/code&gt;オプションが効率的&lt;/li&gt;
&lt;li&gt;TimeMachineを使う場合は、同様にデータのバックアップに重点を置く&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;リカバリー戦略&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;問題発生時：
&lt;ol&gt;
&lt;li&gt;リカバリーモードから起動（⌘+Rキー）&lt;/li&gt;
&lt;li&gt;OSをクリーンインストール&lt;/li&gt;
&lt;li&gt;Migration Assistantを使ってバックアップからデータを復元&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ストレージの選択&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ユーザーデータのみをバックアップする場合、Mac本体と同程度の容量で十分&lt;/li&gt;
&lt;li&gt;頻繁にアクセスする場合はSSD、コスト重視ならHDDを選択&lt;/li&gt;
&lt;li&gt;NASを使用すれば、ネットワーク経由で自動バックアップも可能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;SuperDuperとTimeMachineの選択基準&quot;&gt;SuperDuperとTimeMachineの選択基準&lt;/h2&gt;
&lt;p&gt;私自身はSuperDuperを使用していますが、有償のソフトウェアですしApple謹製の
TimeMachineを使いたい方もいると思います。
次のような観点で選択すればよいと思います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SuperDuperを選ぶ場合&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;バックアップから直接ファイルにアクセスしたい&lt;/li&gt;
&lt;li&gt;標準ファイルシステムでの管理を好む&lt;/li&gt;
&lt;li&gt;選択的なバックアップをしたい&lt;/li&gt;
&lt;li&gt;バックアップスケールや方法を自分で調整したい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TimeMachineを選ぶ場合&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;設定の簡単さを重視&lt;/li&gt;
&lt;li&gt;過去バージョンへの簡単なアクセスが必要&lt;/li&gt;
&lt;li&gt;macOSに標準搭載の機能のみで完結させたい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;まとめ&quot;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;Apple Silicon時代では、起動可能なバックアップよりも「確実にデータを保護し、
必要時に復元できる」戦略が重要です。OSはリカバリーモードから再インストールし
、大切なユーザーデータは確実にバックアップしておくことで、トラブル時も迅速に
復旧できます。&lt;/p&gt;
&lt;p&gt;SuperDuperの最新ベータ版ではブータビリティの改善が進んでいますが、
現実的な運用としてはユーザーデータに焦点を当てた戦略が効率的です。&lt;/p&gt;
&lt;p&gt;また、私はpCloudも使用しているため、
特に重要なファイルはpCloud上で作業するようにしています。
過去30日分のリビジョンが保存されますし、クラウドからいつでも取り出せます。&lt;/p&gt;
&lt;p&gt;家庭用途では、
特にストレージコストを考慮すると、データ中心の定期的バックアップと
クラウドなどへの直近のデータの保管を併用すれば十分な保護が可能です。&lt;/p&gt;
</content>
        <summary type="html">4月に入って日本語対応のApple IntelligenceをサポートしたmacOS Sequoia15.4、4月16日にはセキュリティアップデートのための15.1、その他Command LineToolsのアップデートなどが続いています。これらの更新の前には都度Macのバックアップと取っていますが、改めてMacのバックアップについて検討してみました。</summary>
        </entry><entry xml:lang="ja">
        <title>ChromaLensCLI: Command Line Color Accessibility Validation</title>
        <published>2025-01-25T00:00:00+00:00</published>
        <updated>2025-01-25T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2025/01/chromalenscli-command-line-color-accessibility/" type="text/html"/>
        <id>https://codedchords.dev/blog/2025/01/chromalenscli-command-line-color-accessibility/</id>
        
            <content type="html">&lt;p&gt;とりあえず色覚異常の方にも画像が正しく認識できてるか確認するためのツール &lt;code&gt;ChromaLens CLI&lt;/code&gt;を作ってみた。&lt;/p&gt;
&lt;h2 id=&quot;ChromaLens_CLIの使い方&quot;&gt;ChromaLens CLIの使い方&lt;/h2&gt;
&lt;p&gt;使い方は、次の通り。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./chromalens&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.jpg output.jpg --type tritanopia&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで色覚異常の方が実際どのようにインプットとなる画像が見えているかシミュ
レーションした画像が出力されます。
&lt;code&gt;type&lt;/code&gt; オプションの指定は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;protanopia (1型色覚)&lt;/li&gt;
&lt;li&gt;deuteranopia (2型色覚)&lt;/li&gt;
&lt;li&gt;tritanopia (3型色覚)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;ChromeLens_CLIの環境設定&quot;&gt;ChromeLens CLIの環境設定&lt;/h4&gt;
&lt;p&gt;以下は、venvでPythonの仮想環境を作って必要なパッケージをインストールする手順です。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;python3&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -m venv venv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; venv/bin/activate.fish&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;pip&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install numpy Pillow&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以下が実際のコードです。&lt;code&gt;chromalens&lt;/code&gt;というファイル名で保管ください。授業中に書いたコードなので、エラー処理などは省いています。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;from&lt;/span&gt;&lt;span&gt; PIL&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; import&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;z-10 z-i&quot;&gt; ColorVisionSimulator&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    MATRICES&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;protanopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.567&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.433&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.558&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.442&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.242&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.758&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        ]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;deuteranopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.625&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.375&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.700&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.300&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.300&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.700&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        ]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;tritanopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.950&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.050&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.433&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.567&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;            [&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;0.000&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.475&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.525&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;        ])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; simulate&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; output_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; cvd_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; cvd_type&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; not in&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; self&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;MATRICES&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;            raise&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; ValueError&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Unsupported CVD type. Choose from: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;MATRICES&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # 画像を読み込み&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        img&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # RGBAの場合はRGBに変換&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;mode&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;RGBA&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            img&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;RGB&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # NumPy配列に変換&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        img_array&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # 色変換行列を適用&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        matrix&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-11 z-i&quot;&gt; self&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;MATRICES&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span&gt;cvd_type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        shape&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; img_array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;shape&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pixels&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; img_array&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        converted&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;pixels&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; matrix&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;T&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # 値を0-255の範囲に収める&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        converted&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;clip&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;converted&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 255&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        converted&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; converted&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;reshape&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;shape&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # 画像として保存&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        result&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; Image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;fromarray&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;converted&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;astype&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;uint8&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;RGB&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        result&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;output_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; main&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    parser&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; argparse&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;ArgumentParser&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;Color Vision Deficiency Simulator&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    parser&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;input&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; help&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;Input image path&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    parser&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;output&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; help&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;Output image path&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    parser&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;--type&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;-t&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; choices&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;protanopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;deuteranopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;tritanopia&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;                        required&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; help&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;Type of color vision deficiency&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    args&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; parser&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;parse_args&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    simulator&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; ColorVisionSimulator&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    try&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        simulator&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;simulate&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; args&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; args&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Simulation complete: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    except&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; Exception&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; as&lt;/span&gt;&lt;span&gt; e&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Error: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        exit&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span&gt; __name__&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    main&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        <summary type="html">デザイン入門のようなコースをうけていて「色覚異常の方もいるので気をつけましょうね」とか説明されてんだけど、そんな適当な説明じゃなくちゃんとAccessibilityについて説明しろよと思う。だいたい「気をつける」のでなく、世の中にはちゃんとチェックするツールもあるし・・・・</summary>
        </entry><entry xml:lang="ja">
        <title>How to Merge and Optimize Your PDF Files</title>
        <published>2025-01-10T00:00:00+00:00</published>
        <updated>2025-01-10T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2025/01/how-to-merge-and-optimize-pdf/" type="text/html"/>
        <id>https://codedchords.dev/blog/2025/01/how-to-merge-and-optimize-pdf/</id>
        
            <content type="html">&lt;h2 id=&quot;Bypassing_PDF_Password_Protection&quot;&gt;Bypassing PDF Password Protection&lt;/h2&gt;
&lt;p&gt;While the DJI Neo user manual is provided in PDF format, it&#39;s password-protected, preventing any modifications or merging. Here&#39;s how to bypass the password protection on a Mac:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the PDF in the Preview app&lt;/li&gt;
&lt;li&gt;Select &quot;Print&quot; from the File menu (note: not &quot;Export&quot;)&lt;/li&gt;
&lt;li&gt;Choose &quot;Save as PDF&quot; and specify the destination&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This process will create a new PDF without password protection.&lt;/p&gt;
&lt;h2 id=&quot;Merging_PDFs&quot;&gt;Merging PDFs&lt;/h2&gt;
&lt;p&gt;While this can be done with &lt;code&gt;Ghostscript&lt;/code&gt; (mentioned later), we&#39;ll use &lt;code&gt;pdftk&lt;/code&gt; for its advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preserves PDF internal structure (table of contents, bookmarks, links, etc.)&lt;/li&gt;
&lt;li&gt;Operates at the binary level, preventing quality degradation&lt;/li&gt;
&lt;li&gt;(Although not relevant for this case) Offers flexible operations like page rotation and reordering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s how to merge PDFs:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Install pdftk (on Mac)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install pdftk-java&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Merge PDFs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;pdftk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input1.pdf input2.pdf cat output merged.pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;PDF_Optimization&quot;&gt;PDF Optimization&lt;/h2&gt;
&lt;p&gt;After merging, I found that the file size exceeded 40MB, which was too large to upload to MLIT&#39;s DIPS2.0 (Drone/UAS Information Platform System).&lt;/p&gt;
&lt;p&gt;To resolve this, I used Ghostscript for optimization:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Install Ghostscript (on Mac)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install ghostscript&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Optimize PDF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;gs&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dNOPAUSE -dQUIET -dBATCH -sOutputFile=output.pdf input.pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-dPDFSETTINGS&lt;/code&gt; option allows you to adjust the compression level:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/screen&lt;/code&gt;: For screen display (72dpi) - smallest file size&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/ebook&lt;/code&gt;: For e-books (150dpi) - balanced compression&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/printer&lt;/code&gt;: For printing (300dpi)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/prepress&lt;/code&gt;: For high-quality printing (highest quality)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Making_PDF_Searchable&quot;&gt;Making PDF Searchable&lt;/h2&gt;
&lt;p&gt;Scanned PDFs aren&#39;t searchable. Use these Linux/Mac commands for OCR instead of expensive software:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install ocrmypdf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install tesseract-lang&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# OCR with image optimization&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;ocrmypdf&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -l jpn --optimize&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.pdf output.pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;Creating_Table_of_contents&quot;&gt;Creating Table of contents&lt;/h2&gt;
&lt;p&gt;Scanned PDFs often lack a table of contents. Instead of buying expensive PDF software, use these Linux/Mac commands:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Install pdftk (on Mac)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install pdftk-java&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Extract metadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;pdftk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.pdf dump_data_utf8&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; metadata.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open metadata.txt in your editor and add bookmark locations in this format:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkBegin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkTitle: Chapter 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkLevel: 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkPageNumber: 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkBegin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkTitle: Chapter 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkLevel: 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;BookmarkPageNumber: 10&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then apply the metadata to create a PDF with a table of contents:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Apply edited metadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;pdftk&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; input.pdf update_info_utf8 metadata.txt output output.pdf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;Conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With these steps, I successfully optimized the PDF and completed the drone registration.&lt;/p&gt;
&lt;p&gt;However, it seems rather inefficient to require individual manual submissions for DJI drones, which are widely used. This feels like typical bureaucratic red tape.&lt;/p&gt;
&lt;p&gt;Finally, while this may only be relevant for Japanese users, I&#39;m sharing the prepared DJI Neo manual that meets DIPS2.0 requirements here: &lt;a rel=&quot;external&quot; href=&quot;https://e.pcloud.link/publink/show?code=XZHwGwZuCQmrRb6SFHGinz8MJe1akHv9dF7&quot;&gt;Download&lt;/a&gt;&lt;/p&gt;
</content>
        <summary type="html">I recently purchased a DJI Neo drone, and in Japan, it&#39;s mandatory to register your drone with the Ministry of Land, Infrastructure, Transport and Tourism (MLIT). During registration, you need to submit a user manual that includes both handling instructions and specifications. However, the DJI Neo manual only contains references to specifications on DJI&#39;s website. Therefore, for the registration, I needed to merge the user manual with the specifications from the website.</summary>
        </entry><entry xml:lang="ja">
        <title>A Failed Experiment: Using OpenCV to Read Printed Dates from Scanned Photos</title>
        <published>2025-01-06T00:00:00+00:00</published>
        <updated>2025-01-06T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2025/01/a-failed-experiment-using-opencv/" type="text/html"/>
        <id>https://codedchords.dev/blog/2025/01/a-failed-experiment-using-opencv/</id>
        
            <content type="html">&lt;h3 id=&quot;Organizing_Old_Photographs&quot;&gt;Organizing Old Photographs&lt;/h3&gt;
&lt;p&gt;I recently purchased a scanner at the end of the year to digitize old photographs. The photographs have dates printed on them, and I&#39;ve been manually setting these dates in the EXIF information using ExifTool.&lt;/p&gt;
&lt;p&gt;I use Hazel to organize image files, and I&#39;ve already created a tool that updates file creation dates that Hazel can read with the shooting dates from EXIF data. However, since setting EXIF information manually is time-consuming, I explored the possibility of automating this process using OpenCV.&lt;/p&gt;
&lt;h3 id=&quot;Date_Reading_Tool&quot;&gt;Date Reading Tool&lt;/h3&gt;
&lt;p&gt;Below is a script that uses OpenCV to read dates from images and set them using ExifTool.&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;#!/usr/bin/env python3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;from&lt;/span&gt;&lt;span&gt; datetime&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; import&lt;/span&gt;&lt;span&gt; datetime&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; preprocess_image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    Extract and process the date stamp portion from the image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Load image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    img&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imread&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    height&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; width&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; img&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Crop the bottom right region&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    x1&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; *&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.65&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    y1&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; *&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0.75&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cropped&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; img&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;y1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; x1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Split channels&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    b&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; r&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;cropped&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Enhance red channel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    red_emphasis&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;subtract&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; g&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Enhance contrast using CLAHE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    clahe&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;createCLAHE&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;clipLimit&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; tileGridSize&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    enhanced&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; clahe&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;red_emphasis&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Strong thresholding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    _&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; binary&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;threshold&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;enhanced&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 30&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 255&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;THRESH_BINARY&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Remove noise&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    kernel&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;ones&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;uint8&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cleaned&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;morphologyEx&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;binary&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;MORPH_OPEN&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; kernel&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Detect and filter contours&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    contours&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;findContours&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;cleaned&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;RETR_EXTERNAL&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;CHAIN_APPROX_SIMPLE&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Filter by area&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mask&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; np&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;zeros_like&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;cleaned&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    min_area&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max_area&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 500&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; contour&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span&gt; contours&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        area&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;contourArea&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;contour&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; min_area&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; area&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; max_area&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;drawContours&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;mask&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;, [&lt;/span&gt;&lt;span&gt;contour&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;, (&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Resize to 4x&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;mask&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; None&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; fx&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; fy&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; interpolation&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;INTER_CUBIC&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Save debug images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    debug_dir&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;debug&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;makedirs&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; exist_ok&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    basename&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imwrite&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;cropped_&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; cropped&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imwrite&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;red_emphasis_&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; red_emphasis&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imwrite&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;enhanced_&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; enhanced&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imwrite&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;binary_&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; binary&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    cv2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;imwrite&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;debug_dir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;result_&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;basename&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;),&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    return&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; extract_date_from_image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;Extract date from image using OCR&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # Preprocess image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    processed_image&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; preprocess_image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # OCR configuration for single line text&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    custom_configs&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;--psm 6 -c tessedit_char_whitelist=&amp;quot;0123456789&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;  # Uniform text block&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;--psm 7 -c tessedit_char_whitelist=&amp;quot;0123456789&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;  # Single text line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;--psm 8 -c tessedit_char_whitelist=&amp;quot;0123456789&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;  # Single word&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;#39;--psm 10 -c tessedit_char_whitelist=&amp;quot;0123456789&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;  # Single character&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span&gt; custom_configs&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # Run OCR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        text&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; pytesseract&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;image_to_string&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;processed_image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; config&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        text&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;OCR result (config=&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;): [&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;]&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # Normalize spaces&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        text&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;        # Simple pattern matching&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        pattern&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt; r&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;{2}&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;{1,2}&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;\s&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;\d&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;{1,2}&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        match&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; re&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;pattern&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; text&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; match&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;            try&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                year&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                month&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                day&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-9 z-i&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;                # Handle 2000s&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                year&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 2000&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +&lt;/span&gt;&lt;span&gt; year&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; if&lt;/span&gt;&lt;span&gt; year&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 50&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; else&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1900&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +&lt;/span&gt;&lt;span&gt; year&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;                if&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;=&lt;/span&gt;&lt;span&gt; month&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 12&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; and&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;=&lt;/span&gt;&lt;span&gt; day&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; &amp;lt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 31&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;                    return&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; datetime&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;year&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; month&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; day&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;            except&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; ValueError&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;                continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    return&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; None&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; set_exif_date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;Set EXIF date information using ExifTool&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if not&lt;/span&gt;&lt;span&gt; date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;No date found in: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    date_str&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;%Y:%m:&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;    # EXIF tags to set&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    exif_tags&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;DateTimeOriginal&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;CreateDate&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;        &amp;quot;ModifyDate&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    command&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;exiftool&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;-overwrite_original_in_place&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; tag&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span&gt; exif_tags&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        command&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;-&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;tag&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;date_str&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    command&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    try&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        subprocess&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;command&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; check&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt; capture_output&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Date set successfully: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -&amp;gt; &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;date_str&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; True&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    except&lt;/span&gt;&lt;span&gt; subprocess&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;CalledProcessError&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; as&lt;/span&gt;&lt;span&gt; e&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Failed to set EXIF information: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; False&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; process_images&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;directory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-7&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;Process all images in the specified directory&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    processed&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    failed&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    for&lt;/span&gt;&lt;span&gt; filename&lt;/span&gt;&lt;span class=&quot;z-9&quot;&gt; in&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;listdir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;        if&lt;/span&gt;&lt;span&gt; filename&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;endswith&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;#39;.png&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;.jpg&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;#39;.jpeg&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            image_path&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; filename&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            date&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; extract_date_from_image&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;            if&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt; set_exif_date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;image_path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;,&lt;/span&gt;&lt;span&gt; date&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                processed&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;            else&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                failed&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; +=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;    print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;Processing complete:&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;    print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Success: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;processed&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;    print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7 z-i&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Failed: &lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;{&lt;/span&gt;&lt;span&gt;failed&lt;/span&gt;&lt;span class=&quot;z-16&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt; main&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;():&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt; len&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;sys&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;argv&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Usage: photodate &amp;lt;image_directory&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        sys&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    directory&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; =&lt;/span&gt;&lt;span&gt; sys&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-12 z-i&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;    if not&lt;/span&gt;&lt;span&gt; os&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;isdir&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-14 z-i&quot;&gt;        print&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;&amp;quot;Specified path is not a valid directory&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        sys&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;z-3&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    process_images&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;(&lt;/span&gt;&lt;span&gt;directory&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-9&quot;&gt;if&lt;/span&gt;&lt;span&gt; __name__&lt;/span&gt;&lt;span class=&quot;z-1&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3&quot;&gt;    main&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;Results&quot;&gt;Results&lt;/h3&gt;
&lt;p&gt;I tested the script on 27 scanned photographs.&lt;/p&gt;
&lt;p&gt;Out of 27 images, the script only detected dates in 4 images, and among those, only 1 date was correctly recognized.&lt;/p&gt;
&lt;p&gt;Well, this isn&#39;t practical enough to use.&lt;/p&gt;
</content>
        <summary type="html">I recently purchased a scanner and was challenged to see if I could capture the shooting date from the images when digitizing old photos that have dates printed on them.</summary>
        </entry><entry xml:lang="ja">
        <title>A Command to Sync EXIF Data with File System Timestamps</title>
        <published>2024-12-30T00:00:00+00:00</published>
        <updated>2024-12-30T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2024/12/exif-tool/" type="text/html"/>
        <id>https://codedchords.dev/blog/2024/12/exif-tool/</id>
        
            <content type="html">&lt;p&gt;The scanner&#39;s performance has been impressive - it handled even my old San Francisco business trip photos from 2001 beautifully, producing crisp, clear scans.&lt;/p&gt;
&lt;h2 id=&quot;Setting_EXIF_Information&quot;&gt;Setting EXIF Information&lt;/h2&gt;
&lt;p&gt;Unlike photos taken with a digital camera, scanned images don&#39;t come with proper EXIF data. To preserve the actual capture dates of these photos, I used exiftool from the command line to set the correct EXIF information:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Example: Setting the original capture date&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;exiftool&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; &amp;quot;-DateTimeOriginal=2001:04:15 10:00:00&amp;quot; photo.jpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command successfully embeds the correct capture date in the photo&#39;s EXIF data.&lt;/p&gt;
&lt;h2 id=&quot;The_Photo_Organization&quot;&gt;The Photo Organization&lt;/h2&gt;
&lt;p&gt;For managing my photo files, I rely on Hazel, a powerful file organization tool for Mac. Hazel offers some fantastic automation features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Filtering based on file creation and modification dates&lt;/li&gt;
&lt;li&gt;Automatic folder organization&lt;/li&gt;
&lt;li&gt;Smart file renaming&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, Hazel has one limitation that&#39;s been bugging me: it can&#39;t directly read EXIF data. This means that even though I&#39;ve carefully added the correct capture dates to the EXIF data, I can&#39;t use this information for Hazel&#39;s automated organization.&lt;/p&gt;
&lt;h2 id=&quot;The_Solution:_adj-photodate_Script&quot;&gt;The Solution: adj-photodate Script&lt;/h2&gt;
&lt;p&gt;To bridge this gap, I created a script that syncs EXIF information with the file system timestamps (creation and modification dates). This enables Hazel to organize photos based on their actual capture dates.&lt;/p&gt;
&lt;p&gt;The script provides the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Syncs &lt;code&gt;DateTimeOriginal&lt;/code&gt; from EXIF to file system timestamps&lt;/li&gt;
&lt;li&gt;Includes a dry-run mode for previewing changes&lt;/li&gt;
&lt;li&gt;Handles batch processing of multiple files&lt;/li&gt;
&lt;li&gt;Supports recursive directory processing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Installation&quot;&gt;Installation&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file named &quot;adj-photodate&quot; and paste the following script:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://gist.github.com/yostos/9c301b2831ad3e810cbb4131586687a7&quot;&gt;adj-photodate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make it executable:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;chmod&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; +x adj-photodate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optionally, move it to your path:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;cp&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; adj-photodate ~/bin/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dependencies:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;bash shell&lt;/li&gt;
&lt;li&gt;exiftool&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Usage&quot;&gt;Usage&lt;/h3&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Process a single file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./adj-photodate&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; photo.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Preview changes with dry-run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./adj-photodate&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -n photo.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Process all JPEG files in a directory recursively&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./adj-photodate&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -r ~/Pictures/Scanned&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To see available options:&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;./adj-photodate&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; --help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        <summary type="html">I recently embarked on a journey to digitize my old photographs. Since mypre-digital photos exist only as physical prints, I decided to invest in aPFU scanner to bring these memories into the digital age.</summary>
        </entry><entry xml:lang="ja">
        <title>コマンドラインで画像のExif修正</title>
        <published>2024-08-23T00:00:00+00:00</published>
        <updated>2024-08-23T00:00:00+00:00</updated>
        <author>
            <name>Toshiyuki Yoshida</name>
        </author>
        <link rel="alternate" href="https://codedchords.dev/blog/2024/08/exiftool/" type="text/html"/>
        <id>https://codedchords.dev/blog/2024/08/exiftool/</id>
        
            <content type="html">&lt;p&gt;デジカメの日付の設定を間違えExifの日付を修正したかったが、
AppStoreから余計なアプリをインストールしたくなかったので、Homebrewでexiftoolをインストールして修正。&lt;/p&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Install exiftool using Homebrew&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;brew&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; install exiftool&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Confirm dates of exif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;exiftool&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; R0000001.jpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    ExifTool&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Version Number         :&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 12.76&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Name                       : R0000001.JPG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    Directory&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt;                       : .&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Size                       :&lt;/span&gt;&lt;span class=&quot;z-14&quot;&gt; 12&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; MB&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Modification Date/Time     : 2024:08:26 12:42:29+09:00&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Access Date/Time           : 2024:08:26 12:43:26+09:00&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Inode Change Date/Time     : 2024:08:26 12:43:24+09:00&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Permissions                : -rwx------&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Type                       : JPEG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    File&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Type Extension             : jpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    MIME&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Type                       : image/jpeg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;    Exif&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; Byte Order                 : Little-endian&lt;/span&gt;&lt;span&gt; (Intel,&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; II&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4&quot;&gt;    (&lt;/span&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;Output&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; truncated for brevity&lt;/span&gt;&lt;span class=&quot;z-4&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-4 z-i&quot;&gt;# Modify dates of exif&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-3 z-i&quot;&gt;exiftool&lt;/span&gt;&lt;span class=&quot;z-7&quot;&gt; -Alldates=&amp;#39;2024:08:23 12:11:00&amp;#39; -overwrite_original ./&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content>
        <summary type="html">デジカメの日付の設定を間違えたので、Exifの日付をコマンドラインで一括修正する方法を見つけました。</summary>
        </entry>
</feed>
