<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>J.J.&#39;s Blogs</title>
  
  <subtitle>技術筆記</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://morosedog.gitlab.io/"/>
  <updated>2026-06-13T10:41:52.559Z</updated>
  <id>https://morosedog.gitlab.io/</id>
  
  <author>
    <name>J.J. Huang</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Python | OpenCV 專案：天堂私服遊戲輔助（四）目標優先序選擇</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260422-python-opencv-project-lineage-target/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260422-python-opencv-project-lineage-target/</id>
    <published>2026-04-22T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.559Z</updated>
    
    <content type="html"><![CDATA[<h2 id="⚠️-免責聲明"><a href="#⚠️-免責聲明" class="headerlink" title="⚠️ 免責聲明"></a>⚠️ 免責聲明</h2><p>本文章內容<strong>僅供學術研究與電腦視覺技術學習之用途</strong>，所有程式碼與技術說明均以教育目的為出發點。</p><ul><li>本文作者<font size="4"><font color="red"><u><strong>不提供任何形式的輔助程式販售、散佈或商業服務</strong></u></font>。</font></li><li>本文所有範例程式<font size="4"><font color="red"><u><strong>僅限在自行架設的私有伺服器環境中測試</strong></u></font>，不得用於任何正式營運的線上遊戲伺服器。</font></li><li>使用遊戲輔助程式可能違反個別遊戲的使用者條款,並導致帳號封鎖、法律責任等後果，<font size="4"><font color="red"><u><strong>讀者須自行承擔一切相關風險與責任</strong></u></font>。</font></li><li>本文技術內容若被用於任何違法或損害他人利益之行為，<font size="4"><font color="red"><u><strong>作者概不負責</strong></u></font>。</font></li><li>私有伺服器的架設與使用涉及遊戲著作權相關法律問題，讀者應自行評估所在地區的法規，並確認於合法範圍內使用。</li></ul><h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260421-python-opencv-project-lineage-detection"><strong>（三）YOLOv8 怪物偵測整合</strong></a> 中，我們完成了雙執行緒偵測架構，並做出 <strong>live（bbox）／ grid（扁平菱形小地圖）</strong> 兩種預覽模式，也支援執行中熱換模型。</p><p>本篇加入 <strong>目標優先序選擇</strong>：當畫面同時出現多隻怪物時，依使用者設定的<strong>優先類別順序</strong>與<strong>距離畫面中心的遠近</strong>，選出最值得攻擊的目標，並在 live 與 grid 兩種預覽中都以紅色特別標記。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><p><strong>系列總覽：</strong></p><table><thead><tr><th>篇次</th><th>主題</th></tr></thead><tbody><tr><td><a href="/python-opencv-20260419-python-opencv-project-lineage-assistant">一</a></td><td>主程式 GUI 框架與 HP/MP 血量監控</td></tr><tr><td><a href="/python-opencv-20260420-python-opencv-project-lineage-auto-potion">二</a></td><td>自動補藥</td></tr><tr><td><a href="/python-opencv-20260421-python-opencv-project-lineage-detection">三</a></td><td>YOLOv8 怪物偵測整合</td></tr><tr><td><strong>本篇（四／最終篇）</strong></td><td>目標優先序選擇</td></tr></tbody></table><h2 id="🎯-本篇目標"><a href="#🎯-本篇目標" class="headerlink" title="🎯 本篇目標"></a>🎯 本篇目標</h2><ul><li>建立 <code>target_selector.py</code>：依「類別優先清單 + 距離畫面中心」排序，從偵測結果挑出最優先目標</li><li>微調 <code>detector.py</code>：<code>render()</code> / <code>render_grid()</code> 接收 <code>target</code>，讓 <strong>live 模式在鎖定目標框上畫紅色</strong>、<strong>grid 模式把鎖定格塗紅</strong></li><li><code>main.py</code> 更新：<code>SharedState</code> 新增 <code>target</code> 欄位；<code>detection_loop</code> 呼叫 <code>TargetSelector.select()</code> 後把結果餵進 <code>render()</code></li><li>UI 新增「優先類別」輸入欄（逗號分隔），預覽區底部顯示當前鎖定目標；<strong>優先清單可執行中熱切換</strong></li></ul><h2 id="🗂️-本篇新增／更新檔案"><a href="#🗂️-本篇新增／更新檔案" class="headerlink" title="🗂️ 本篇新增／更新檔案"></a>🗂️ 本篇新增／更新檔案</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">lineage_assistant&#x2F;</span><br><span class="line">├── main.py                   ← 更新（SharedState + Thread 2 + UI + _poll 推送優先清單）</span><br><span class="line">├── window_capture.py         ← 不變</span><br><span class="line">├── hp_monitor.py             ← 不變</span><br><span class="line">├── auto_potion.py            ← 不變</span><br><span class="line">├── detector.py               ← 更新（render &#x2F; render_grid 接收 target）</span><br><span class="line">├── target_selector.py        ← 新增（本篇重點）</span><br><span class="line">├── calibrate_game_roi.py     ← 不變</span><br><span class="line">└── models&#x2F;</span><br><span class="line">    └── lineage_detector.pt</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-lineage-target/target-architecture.svg" alt="Python - 圖 1 (target architecture)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-target/target-selection-flow.svg" alt="Python - 圖 2 (target selection flow)"></p><h2 id="💻-目標優先序模組：target-selector-py"><a href="#💻-目標優先序模組：target-selector-py" class="headerlink" title="💻 目標優先序模組：target_selector.py"></a>💻 目標優先序模組：target_selector.py</h2><p>排序規則（由高到低）：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><ol><li>類別在優先清單中的位置越靠前，優先度越高</li><li>未列入清單的類別排最後</li><li>同優先度時，距離畫面中心越近越優先（減少滑鼠移動距離）</li></ol><blockquote><p>Thread 2 每秒會跑幾次 <code>select()</code>，若目標每幀都寫 log 會洗版，所以這裡用 <code>_last_key</code> 只在「目標實際切換」時才寫一筆。</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># target_selector.py</span></span><br><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Callable, Dict, List, Optional</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TargetSelector</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,</span></span></span><br><span class="line"><span class="function"><span class="params">                 priority_classes: Optional[List[str]] = None,</span></span></span><br><span class="line"><span class="function"><span class="params">                 min_conf: float = <span class="number">0.0</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 log_fn: Optional[Callable[[str], None]] = None)</span>:</span></span><br><span class="line">        <span class="comment"># detector.conf 已先做過一道信心度過濾；這裡 min_conf 預設 0 不重複過濾，</span></span><br><span class="line">        <span class="comment"># 想只鎖定高信心目標時才拉高</span></span><br><span class="line">        self.priority  = list(priority_classes <span class="keyword">or</span> [])</span><br><span class="line">        self.min_conf  = min_conf</span><br><span class="line">        self._log      = log_fn <span class="keyword">or</span> (<span class="keyword">lambda</span> msg: <span class="literal">None</span>)</span><br><span class="line">        self._last_key = <span class="literal">None</span>   <span class="comment"># (name, center) — 只在目標切換時寫日誌</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">select</span><span class="params">(self, detections: List[Dict],</span></span></span><br><span class="line"><span class="function"><span class="params">               frame_center: Optional[tuple] = None)</span> -&gt; Optional[Dict]:</span></span><br><span class="line">        <span class="string">"""從偵測結果中選出最優先目標；無可選目標則回傳 None"""</span></span><br><span class="line">        valid = [d <span class="keyword">for</span> d <span class="keyword">in</span> detections <span class="keyword">if</span> d[<span class="string">"conf"</span>] &gt;= self.min_conf]</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> valid:</span><br><span class="line">            <span class="keyword">if</span> self._last_key <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                self._log(<span class="string">"目標：無"</span>)</span><br><span class="line">                self._last_key = <span class="literal">None</span></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">sort_key</span><span class="params">(d)</span>:</span></span><br><span class="line">            name = d[<span class="string">"name"</span>]</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                prio = self.priority.index(name)</span><br><span class="line">            <span class="keyword">except</span> ValueError:</span><br><span class="line">                prio = len(self.priority)     <span class="comment"># 未列出的類別排最後</span></span><br><span class="line">            dist = <span class="number">0.0</span></span><br><span class="line">            <span class="keyword">if</span> frame_center:</span><br><span class="line">                cx, cy = d[<span class="string">"center"</span>]</span><br><span class="line">                dist = math.hypot(cx - frame_center[<span class="number">0</span>], cy - frame_center[<span class="number">1</span>])</span><br><span class="line">            <span class="keyword">return</span> (prio, dist)</span><br><span class="line"></span><br><span class="line">        valid.sort(key=sort_key)</span><br><span class="line">        target = valid[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">        key = (target[<span class="string">"name"</span>], target[<span class="string">"center"</span>])</span><br><span class="line">        <span class="keyword">if</span> key != self._last_key:</span><br><span class="line">            self._log(</span><br><span class="line">                <span class="string">f"🎯 鎖定目標：<span class="subst">&#123;target[<span class="string">'name'</span>]&#125;</span> "</span></span><br><span class="line">                <span class="string">f"(<span class="subst">&#123;target[<span class="string">'conf'</span>]:<span class="number">.2</span>f&#125;</span>) @ <span class="subst">&#123;target[<span class="string">'center'</span>]&#125;</span>"</span></span><br><span class="line">            )</span><br><span class="line">            self._last_key = key</span><br><span class="line">        <span class="keyword">return</span> target</span><br></pre></td></tr></table></figure><h2 id="🔧-detector-py"><a href="#🔧-detector-py" class="headerlink" title="🔧 detector.py"></a>🔧 detector.py</h2><p>Part 3 的 <code>annotate()</code> 本來就能接 <code>target</code>（鎖定的框畫紅色），只是 <code>render()</code> 派發器沒把它傳下去、<code>render_grid()</code> 也還沒用到。本篇補兩件事：</p><ul><li><code>render()</code> 加上 <code>target</code> 參數並派發下去</li><li><code>render_grid()</code> 新增 <code>target</code> 參數，命中鎖定目標的那一格填<strong>紅色</strong>（其他怪物仍是黃色）</li></ul><p>以下是完整的更新後 <code>detector.py</code>（imports 與模組常數 <code>VIEW_EW</code> / <code>VIEW_NS</code> / <code>GRID_HALF</code> / <code>CANVAS_W</code> / <code>CANVAS_H</code> / <code>get_game_roi</code> / <code>get_tile_px</code> 沿用 Part 3，改動處用 <code># ← 本篇新增</code> 標註）：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># detector.py</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Dict, List, Literal, Optional</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> window_capture <span class="keyword">import</span> (get_game_roi, get_tile_px,</span><br><span class="line">                            TILES_PER_HALF_WIDTH, TILES_PER_HALF_HEIGHT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 菱形視野邊界（把螢幕上「左右 ±8 / 上下 ±10」換算回 iso 世界座標）</span></span><br><span class="line">VIEW_EW    = <span class="number">2</span> * TILES_PER_HALF_WIDTH    <span class="comment"># 東西邊界：|wx + wy| ≤ 16</span></span><br><span class="line">VIEW_NS    = <span class="number">2</span> * TILES_PER_HALF_HEIGHT   <span class="comment"># 南北邊界：|wy - wx| ≤ 20</span></span><br><span class="line">GRID_HALF  = (VIEW_EW + VIEW_NS) // <span class="number">2</span>    <span class="comment"># 迴圈迭代半徑（菱形 4 角 |wx|/|wy| 最大 = 18）</span></span><br><span class="line"></span><br><span class="line">CANVAS_W   = <span class="number">200</span>                         <span class="comment"># 小地圖畫布寬（視野寬 16·DW = 192，加一點邊距）</span></span><br><span class="line">CANVAS_H   = <span class="number">130</span>                         <span class="comment"># 小地圖畫布高（視野高 20·DH = 120，加一點邊距）</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Detector</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, model_path: str, conf: float = <span class="number">0.5</span>)</span>:</span></span><br><span class="line">        self.model   = YOLO(model_path)</span><br><span class="line">        self.conf    = conf</span><br><span class="line">        self.enabled = <span class="literal">True</span></span><br><span class="line">        self.mode: Literal[<span class="string">"live"</span>, <span class="string">"grid"</span>] = <span class="string">"live"</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">detect</span><span class="params">(self, frame)</span> -&gt; List[Dict]:</span></span><br><span class="line">        <span class="string">"""回傳偵測結果，每筆為：</span></span><br><span class="line"><span class="string">        &#123;"name": str, "conf": float,</span></span><br><span class="line"><span class="string">         "box": (x1, y1, x2, y2), "center": (cx, cy)&#125;</span></span><br><span class="line"><span class="string">        """</span></span><br><span class="line">        results = self.model(frame, conf=self.conf, verbose=<span class="literal">False</span>)</span><br><span class="line">        detections = []</span><br><span class="line">        <span class="keyword">for</span> r <span class="keyword">in</span> results:</span><br><span class="line">            <span class="keyword">for</span> box <span class="keyword">in</span> r.boxes:</span><br><span class="line">                name = self.model.names[int(box.cls)]</span><br><span class="line">                c    = float(box.conf)</span><br><span class="line">                x1, y1, x2, y2 = map(int, box.xyxy[<span class="number">0</span>])</span><br><span class="line">                cx, cy = (x1 + x2) // <span class="number">2</span>, (y1 + y2) // <span class="number">2</span></span><br><span class="line">                detections.append(&#123;</span><br><span class="line">                    <span class="string">"name"</span>: name, <span class="string">"conf"</span>: c,</span><br><span class="line">                    <span class="string">"box"</span>: (x1, y1, x2, y2), <span class="string">"center"</span>: (cx, cy)</span><br><span class="line">                &#125;)</span><br><span class="line">        <span class="keyword">return</span> detections</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">render</span><span class="params">(self, frame, detections: List[Dict],</span></span></span><br><span class="line"><span class="function"><span class="params">               target: Optional[Dict] = None)</span>:</span>      <span class="comment"># ← 本篇新增 target 參數</span></span><br><span class="line">        <span class="string">"""主執行緒呼叫入口：依 mode 派發到 annotate / render_grid，並把 target 傳下去"""</span></span><br><span class="line">        <span class="keyword">if</span> self.mode == <span class="string">"grid"</span>:</span><br><span class="line">            <span class="keyword">return</span> self.render_grid(frame, detections, target=target)   <span class="comment"># ← 本篇新增</span></span><br><span class="line">        <span class="keyword">return</span> self.annotate(frame, detections, target=target)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">annotate</span><span class="params">(self, frame, detections: List[Dict],</span></span></span><br><span class="line"><span class="function"><span class="params">                 target: Optional[Dict] = None)</span>:</span></span><br><span class="line">        <span class="string">"""live 模式：在原畫面上繪製偵測框（鎖定目標畫紅色，其他畫綠色）"""</span></span><br><span class="line">        out = frame.copy()</span><br><span class="line">        <span class="keyword">for</span> d <span class="keyword">in</span> detections:</span><br><span class="line">            x1, y1, x2, y2 = d[<span class="string">"box"</span>]</span><br><span class="line">            color = (<span class="number">0</span>, <span class="number">0</span>, <span class="number">220</span>) <span class="keyword">if</span> (target <span class="keyword">and</span> d <span class="keyword">is</span> target) <span class="keyword">else</span> (<span class="number">0</span>, <span class="number">220</span>, <span class="number">0</span>)</span><br><span class="line">            cv2.rectangle(out, (x1, y1), (x2, y2), color, <span class="number">2</span>)</span><br><span class="line">            label = <span class="string">f"<span class="subst">&#123;d[<span class="string">'name'</span>]&#125;</span> <span class="subst">&#123;d[<span class="string">'conf'</span>]:<span class="number">.2</span>f&#125;</span>"</span></span><br><span class="line">            cv2.putText(out, label, (x1, y1 - <span class="number">6</span>),</span><br><span class="line">                        cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.5</span>, color, <span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> out</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">render_grid</span><span class="params">(self, frame, detections: List[Dict],</span></span></span><br><span class="line"><span class="function"><span class="params">                    target: Optional[Dict] = None)</span>:</span>  <span class="comment"># ← 本篇新增 target 參數</span></span><br><span class="line">        <span class="string">"""grid 模式：以扁平等角菱形格呈現怪物相對方位；鎖定目標格填紅"""</span></span><br><span class="line">        fh, fw = frame.shape[:<span class="number">2</span>]</span><br><span class="line">        rx, ry, rw, rh = get_game_roi(fw, fh)</span><br><span class="line">        px, py = rx + rw / <span class="number">2</span>, ry + rh / <span class="number">2</span>    <span class="comment"># 玩家像素座標（ROI 中心）</span></span><br><span class="line">        tw, th = get_tile_px(fw, fh)          <span class="comment"># 一格地磚像素：roi_w/16, roi_h/20（分母不同！）</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 偵測中心 → 世界格座標；同時記錄 target 落在哪一格（本篇新增）</span></span><br><span class="line">        monsters    = set()</span><br><span class="line">        target_cell = <span class="literal">None</span>                                      <span class="comment"># ← 本篇新增</span></span><br><span class="line">        <span class="keyword">for</span> d <span class="keyword">in</span> detections:</span><br><span class="line">            cx, cy = d[<span class="string">"center"</span>]</span><br><span class="line">            dx, dy = cx - px, cy - py</span><br><span class="line">            wx = round(dx / tw - dy / th)</span><br><span class="line">            wy = round(dx / tw + dy / th)</span><br><span class="line">            <span class="keyword">if</span> abs(wx + wy) &lt;= VIEW_EW <span class="keyword">and</span> abs(wy - wx) &lt;= VIEW_NS:</span><br><span class="line">                monsters.add((wx, wy))</span><br><span class="line">                <span class="keyword">if</span> target <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> d <span class="keyword">is</span> target:          <span class="comment"># ← 本篇新增</span></span><br><span class="line">                    target_cell = (wx, wy)</span><br><span class="line"></span><br><span class="line">        canvas = np.full((CANVAS_H, CANVAS_W, <span class="number">3</span>), <span class="number">26</span>, dtype=np.uint8)</span><br><span class="line">        ccx, ccy = CANVAS_W // <span class="number">2</span>, CANVAS_H // <span class="number">2</span></span><br><span class="line">        DW, DH   = <span class="number">12</span>, <span class="number">6</span></span><br><span class="line"></span><br><span class="line">        WHITE, BLACK  = (<span class="number">240</span>, <span class="number">240</span>, <span class="number">240</span>), (<span class="number">16</span>, <span class="number">16</span>, <span class="number">16</span>)</span><br><span class="line">        YELLOW, EDGE  = (<span class="number">0</span>, <span class="number">220</span>, <span class="number">255</span>), (<span class="number">95</span>, <span class="number">95</span>, <span class="number">115</span>)</span><br><span class="line">        RED           = (<span class="number">0</span>, <span class="number">0</span>, <span class="number">220</span>)                             <span class="comment"># ← 本篇新增：鎖定目標色</span></span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">diamond</span><span class="params">(wx, wy, fill, edge)</span>:</span></span><br><span class="line">            cx = ccx + (wx + wy) * DW / <span class="number">2</span></span><br><span class="line">            cy = ccy + (wy - wx) * DH / <span class="number">2</span></span><br><span class="line">            pts = np.array([</span><br><span class="line">                [cx,          cy - DH / <span class="number">2</span>],</span><br><span class="line">                [cx + DW / <span class="number">2</span>, cy         ],</span><br><span class="line">                [cx,          cy + DH / <span class="number">2</span>],</span><br><span class="line">                [cx - DW / <span class="number">2</span>, cy         ],</span><br><span class="line">            ], dtype=np.int32)</span><br><span class="line">            <span class="keyword">if</span> fill <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                cv2.fillConvexPoly(canvas, pts, fill)</span><br><span class="line">            cv2.polylines(canvas, [pts], <span class="literal">True</span>, edge, <span class="number">1</span>, cv2.LINE_AA)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> wx <span class="keyword">in</span> range(-GRID_HALF, GRID_HALF + <span class="number">1</span>):</span><br><span class="line">            <span class="keyword">for</span> wy <span class="keyword">in</span> range(-GRID_HALF, GRID_HALF + <span class="number">1</span>):</span><br><span class="line">                <span class="keyword">if</span> abs(wx + wy) &gt; VIEW_EW: <span class="keyword">continue</span></span><br><span class="line">                <span class="keyword">if</span> abs(wy - wx) &gt; VIEW_NS: <span class="keyword">continue</span></span><br><span class="line">                <span class="keyword">if</span> wx == <span class="number">0</span> <span class="keyword">and</span> wy == <span class="number">0</span>:</span><br><span class="line">                    diamond(wx, wy, BLACK, WHITE)               <span class="comment"># 玩家</span></span><br><span class="line">                <span class="keyword">elif</span> (wx, wy) == target_cell:                   <span class="comment"># ← 本篇新增分支</span></span><br><span class="line">                    diamond(wx, wy, RED, WHITE)                 <span class="comment">#    鎖定目標：紅底 + 白邊</span></span><br><span class="line">                <span class="keyword">elif</span> (wx, wy) <span class="keyword">in</span> monsters:</span><br><span class="line">                    diamond(wx, wy, YELLOW, EDGE)               <span class="comment"># 其他怪物</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    diamond(wx, wy, <span class="literal">None</span>, EDGE)                 <span class="comment"># 空格</span></span><br><span class="line">        <span class="keyword">return</span> canvas</span><br></pre></td></tr></table></figure><h2 id="💻-執行緒架構（本篇）"><a href="#💻-執行緒架構（本篇）" class="headerlink" title="💻 執行緒架構（本篇）"></a>💻 執行緒架構（本篇）</h2><p>相較 Part 3，差異只在 Thread 2 多繞一趟 <code>TargetSelector.select()</code>；Thread 1 與 Main 完全不變。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Worker Thread 1 (monitor_loop)                ← 不變</span><br><span class="line">  └── capture_window → HPMonitor.read() → potion.check()</span><br><span class="line">                     → frame_q.put_nowait(frame)</span><br><span class="line"></span><br><span class="line">Worker Thread 2 (detection_loop)              ← 本篇更新</span><br><span class="line">  └── frame_q.get() → detector.detect()</span><br><span class="line">                    → selector.select(detections, frame_center)   ← 新增</span><br><span class="line">                    → detector.render(frame, detections, target&#x3D;target)</span><br><span class="line">                    → SharedState.detections &#x2F; target &#x2F; preview</span><br><span class="line"></span><br><span class="line">Main Thread (tkinter)</span><br><span class="line">  └── root.after(200ms) → _poll → 更新 HP&#x2F;MP + 預覽 + 當前目標 label</span><br><span class="line">                                → 即時把優先清單推回 TargetSelector</span><br></pre></td></tr></table></figure><h2 id="💻-主程式：main-py（最終篇版本）"><a href="#💻-主程式：main-py（最終篇版本）" class="headerlink" title="💻 主程式：main.py（最終篇版本）"></a>💻 主程式：main.py（最終篇版本）</h2><div class="secret-block" data-key="supporter" data-iter="200000" data-content-iv="DiJAgeTYkcq4TcWY" data-content="hBCnVuQnVPS16vV0hyE2MYzep5WvYyS5gqawPM1Z/Y17WC9CMKh4YwwsoEMyfnncrsbPs1+FzUSEZBeT33Yl7/Hw/YHytxXnNMaVN8F+pXdHeH/2dc/edsb7AutvcWtvnQeCFpKpBatiTcuFrPX3NxRWqYZuGgeMqrSmJs3fG8P48ID32kBRW56pYefpJ6kiZzYaT8gWbBwo5OncGyo9t6vNXdHhpW8QQUXNZI1jMf8bSTivfk36tGXWeaLXz4Rft4kVOa54UBVq+FBJMQIZUXKBKFharOwzgwfrS0oQ2DapzXR4qoOpZ/88pXvb38d2VVYaSBRui3mP4New5VuzxOqN/J6oLvU//nKchsHgwlRTUEnTRiF/CbltUu5rIMJrX+Va/+kBGTXl9LbULwt4X6PqPkVajNH2n0NTMDZB06k9fY7JQguqfcH6J7XYjOvhukzDfMJPpF1tQfOyDwAP/39T3CPv1aBYot2nCWfx/EbSOQyX+Nb3Ksue2NN3Wn20V7n75wGHq6BfX8jFPMxWQcQH0l63CN9VTeRTlMYZKYZJ6HvPf9YZFvsnEqi7M58YT7+g2Bt2zanyeXZyidaZ5hZjyUR/uO6NZSjFe64sgI5mCyGXk6WpyzkmqtWJjPXviPpH4c+07wV7+hv0g1iXihu6CXqJj+Us7EHFPbg1nXZW4WtaGi7vDClQCObEONMRQ8Hr8qjUyPyOJIsyKmbxb9vEaMIvYgrEdmeSq7LwYkbPJ7Zhd1WldIf8CLPO43XZqDh3ImlbCwYYGAR9ZPAMAb4RdddDY390mHZBYLXWsANMzvpzNObEEEdPOZLj0FvvaG5g6cQiO5WqLZvDjdf8AQyLAkDmWHujJzdlG/zpxY0P7im6MggpinvzECOaFpYp2gXA+5tK+CODuJrqo6qjn1iWNUDaS0FPCAlYwd48bS8ZX7QxWrlE356dUnAYQfT3ng6sku2k3jU+HYQLN/eKoWeAfgSUZI3XIAOZE//MJQsROWGkyJk1v6KCCAmcRqgUgpxyg5qcDLCopgQrp1B3di9UVXorznWU5ZLgvXdxdrIFXEPZip64wbNUjP8pU9kXtYDnnOzXQv2Lym5MM9BXSMlMqJt7oZFv2jKP4/60itB8ZW9UaoPpghqi/7zwr2YhGYyAMunI39wamyFJukHBlnBSu6OPHCi73RO0XFWe8nzH7uYzXI0W9HhxO3l3Cb/mwYQWIHdvYWv0YOGa0384AbJ0+hPD9fkV3vXge1NkhgDVuj70DfNNVVHuwDvL4DV85dZ01RuvDsyvALyv9678aEIs8xGbA1s4lucouEO11VPQ0UwjCK9Ap3bl1D/vIrcMjWjhHWha5xKpJ67i6nA+s1LF7qfd64Ckpfsf5trYnDBicwFDhzmSfFCnyZ2ibsPjTjdidhAOQVmVeqXaOjQwHLKOg0QCqi1t6aHbSwPRKMmZbHp6YSYk1CFjVmFr9y/B9GuoPdVNW93kLeJZTR9M+/uTq52MJLB2UOdegZ0nyLPs7WWncK1dL0BPUoQWgqfHuRo0Q+9aGCp6UZCtrVdwQbTkEd2wWHsRyWMxRmh6ESavT7toMcD/84nrGJV9B2oXt8exYyiiaIaqCoZn6u/dGbgPkEsKYhKHJkIfUdkzX6Ni9z4umpWq5xzAX3T4XyZ++LP/1Dbq75P9rQHOPKFnKkOQeVEx/Cf5BdXEEuFSyPJEO1seBsMQzsTq1AhvTKT7aYCVwb3F0cVJCbXjhbluPTSdLV0F2SLBgZCR2A1FVYTqivaDC3Em0IJbS2Cq4vlZu9UrTHt1eiS8QHvimYVRFjksVe11dMbkr4Q/7+VBg95mzsOAJb4L2HNo6sld3ribta8Zq6UysWJdgswcF7ZR7dUAi7ms6AI0vp0IAYivB58Xk0ABueUY+l10fP16Wmi52CS0Dmrd4N9QYxPTCBfn9szbHLZNkfObef6THYf7ACQ3T3HoHpMDTSBnAPmhOxFnK9cOrNUC690Py24EdjkXbx9oSWSVxBR9uhK7GAhoZwfT/olncexiEonkVUmlYsLO3c9Gzc9hUWsz8yBvSBYAk+MyQsU+mo64hLntK+6PsuUynK98weZl0yGz7R88uIVAoHoc1OgA9XAPLn/PPeAJkkJO4JVAoEOLuG83Oa9OK8CMfnr3iUbc36HbhSX8loj4mx8J1UbSLYx6lolVlm2mu36+5Wi4F0d6iJ5/2lnD7EpeeKZY2NTGpSeEJiksgpYWwHGZOGfLc43KiAWrZcszadUa+L25fsb/YmlFDZoYKKDYhL9NFmkrJI2wMYOlnNW4tWqn6UbgAVt2AKY7IlmVXSGXsdydYb4uJG9w6+llvEtjm3LRtPAvvq1QmR3Etx2f9CNh4s8AjZzWx6DQYdmu1wIAgMwxASjVQKFcH59PSnFM0oNmW3unfRv1o/qFeLrWjpdhzIGmcmTkNBRzgSaxED3q0BTrRYs9sPMQgfWDd1q5rUZlxskFut6rNazAJcYUBXC5+o7InsGHmK9gQsm9x6LO1n8NHsmcr9ZTjkOzTb3v+OROtQU49v4mym6RR/4f67Pm3LOMa5oexG1bh5ZftkQhqKcknOZ/kn8tQOeEyUcgjkQbhQRodx+KuDcZrG4ezrsuKpKtFL6DGe8iBj8jbsog6B2mRUeiUs5/sgPTENCOhyPikJ83+HT4P1LplM9cTOgDuus03FG7Apcy1Hbi6HYyE/pF/rjNhlsTo880obwHZCJZ7eA2hmrVe7qjM1qcv/XYUg6SAMcNQ+J9qMdhimS8Wp2Jdg7mKTdOboO/PWsrRwcmfh2M5SzSagl9HUh0l0IWWFfDgJBzOtRbReOCIaI91sRqswjLygQuJyAZ2f2KQ6w+wGy+ghpOahwISfX+tKFLzf+aczCmJvrjT4j8iAx3b6ErURvNvfQ1ewoxMGgtpbB2FxtlTWx90iGHm7h1ejAWUuBj/gb2KG60sNUUX79/sgdSXwVsShFyf8RimAGbmCVp//U39XLxLOSeLc/ifOnWw1Iee5cXWakk9g4qvWvikY78qhMuvCV/EA0mcaEeMKmukVdU/TVVzg9KUZM93B7H5/B3gY3a1cjLmVNqZk/rtlcRrTdUb7nIY8D0ne9ICaDuz7MAD+qpbkt+UZKkdLXT4oQ9hhsxI9UuRKNyKQu7SvhqGJIisvaYjxbMcdzK5LodXPdkDh2qtZnqK+/fHnTreJfrCf21QzvauvIqMVrmBXr4bMipChoZANUQh0mz/J3U+5FwiYBVmnu8jNzeuK+ub1cww1SvcJCqQ1nkcf7Xn80LviUVtCPL4qT/hDLudYZIAAQEM/nKsE0Jqnrx0+MXXKO5l2IepbFwwGm6Ttcgpt6n7svCcirtNoisPUygIq597lVFAiaomcBj7rGoGjyMoCJ2R9ylJ91bmOWyJBaqvOvMevxk3IRYMOGV0rYFwYGUfyKWM6BjUP9csCE3C2r6q4WcLUsdqQV8K+wCISOp3aMX6OxdRV8D/BfRVOHYZmg3vgGvXko9esZs3QdxO8quAUE54q4YDV56cjklSfSO2qG45/KVotX+BVgW2fgldaCLDP0xMegEkQT2JtG+V/k4nsfCezqv1veuqs5ZueBVB9aNW2xSuNXME/wv/aNmR3I3pjrqyAUKQ93xBYDjDW5tBmxuPHDSwl/ciJ73n3ZS6m9Q8rnLzxncyZBoY/eKLJnaP0NSDItYLMudfEhB0H/iNFWv55iMtnHT3N4CnFvj4a4GL2Zqui2VeMhF2+Ha5Qe9OsCScZQvt2pfNourx3V+TrfMjlTCNVShGvLGnFYxTIiPMb5wgXC7PQDuvSxCQcaWLHf0NxsGMAHpvz0x7Ufq6VzhYTG4f2cNT2EmSMxfh9MR9Ln+YCVVBXcxQvt/tbwrJ5rF6EQUQJbqmD4xxno2p9Nyn+JK6L34uKItez6clZtfj6Z0u2LAm1uK4r5cfF+gSrfZAJpWrY14Xw8wn5bi/gTF1HzH8zloIJfBc6uqZKdUw/r6b0fzMWgyTD4o5K+o3pHc3JN2ZBwe2R2+cWAjfosXO/G5mFXhwSkg015bGIA9rtnPbiKcXbq2eZJpx8/9jwxDzKmPBxe5vnToqkaU0KM36B3lKUtdPlR4wksAxl4oEB2w0uYifaTtKyxOqFqocvGCRgNDZg9fmcihiu98OWpCQJEWpigJpZdwAlRz2+Raxrpyps02mtJH7oNRwHfeTBpUgEI4Cm4RBOLL69f7+egLETPAq6PuCNPreE74G91h54PFdfcCTuI21SjogqfLqRqRYornYNzEymlZ93r+YCgfA+1ZJkcJq9p38Y1ZyjBGEfMOaao8bKGwEDxbPJqHX4/bQgqrzccLkS/zPoCVmjFXMD1M34rfSYZpKqRbflRuJ50GJr2Kz2q6n9+OqmpSzmAKr0p64cOrfcYk4UNpPH+4FZUW+g/xN8oo6VpAgiql1Fo8TEWC3+2eyAUL84FckFCX75wYRPFyLzp8/ErcqNpP4A93fsJejnpKZvSuxItzYR4ecLPIqUPnwOjfc1UecOmSwgVjKbpZaPYy3eDEXfQcDOgGm60TEHWHYkrs+YsuYQvtfM6qXKcOJauwAzN/Xg+nT7IjGNjZsx/rUu8Pc+vUFIuccoKMIR9fYjyl9bFBgmGEj/z7HhhFICjBG3Jcu++YX/ew6FpaSJSGmnbM5OYF7yK7xZJ5uu8FZQVQEB/TtOTE4K4STjcQOemH4AbTHMpOLpqdeRF4WZuxEOZql/zLJRIfa9Nx27XpKvH+xsmC1t5f49J1WHsPmozlJP324cwLuYFTESqFqLeDR7r8lZJyrgR74tDjWSJLlp+oT8ZnlPZC7PHAnnzQyC1jVYAMO54nmIHC48CdZsr4fP84d/+A3wiXwF5d0VBzz9TimGUPNJHqlLSBeOYsePV/aw8uc6HhdRknXLZczfOc5Bm0L64C2HWDprBZezNEEa7erLR3ST9BVxYsqhIrB/zJNTIx0RDT3PSGlUiPzhHSReekmGmr6kszlA0SWZTmlenznBy9nf4/a0JRCb0Jjx6SC2FDkZcBfJZ43vnkQD9YhTdc4LxbtVsdQHsZDqgPqZQy06EHOVbA+SKld5MRRo9f3nFGvTR4BU34GyL0mfvMBnS2ZujlHEcoyPask2oSuaFCNfcxtDXbsmV4nVqzgfEtEMX4baXAKdmHHH1Xl8sikwhVv1rNRnpjtRqCz8ydCZC1SBIRJNxCXmMTwV0cnY06G+pWtHbanxw4kbUWiN6nk4twn83CsZqLcKUiZGIr0d+87CL22SC+mvgzzkPtzW8Dn8YFU6kX2Xa426qo8JDoeBXDeM2G/TVdFEV1btfkrMDQUDhLuNmFTt2QjUfvfSaxgUASNUgT77GlEzeywFYG4Lt+IQfoEXqPFPDBKW78Kr4pcjTuenYG+8IQGk2VidTlGM2lZ9lN/a1W+KbnP0A9UVKY5m03WFn3W8ymXVOIyICcfQrZsw4C1Jek369crN1Xc8/z/jrhJ32ndT4vJxpeUmFqyP2zzwZfMJ8TZorNUsg9PAYsJwzuDEN/zWFEAdndHMaJHJiTIQeu3QX0oy3NM/7ejZi9c1kj45w1E2F5da8RaGnok/mcMJ2sOwSo8k9UGN8MgASPrsQZ48MLnW3FpIITMZog3EmvVBrdFaJrVW5z8GRm4pkY+T8t5jpdRKExM0cdrEOSzW2fsnQZQFIkrxarmf6mNgOEuAU19PsQnGtt8Lsz1RNSj8eRj3CfO4kp1W5NRpeWBAbg8QELNkMWVueZDzRyPD1XaAqOtj2SxnXhl8IyuRB9sPWdx8F2DjJGpLYtS2P/gv2R/YJpfU8QVifa9C5FGipViSSEZLjbngQk7yyyrhEJ7eXvLVDabXVpXEvUxN7tYfMboI/UE2fFRmDNmL2pwb4TPha/OiPHOS4kAr5QyNbV5a3Yroq/535oOUbRcyt8OmHIswlkgC5yK6jOWVgNMvMdnROZ1EQ4gAnBHGAQKSZFGB4vtjfL/QqABNfs86dZ+ceKzTLXDb/oMNJ0hCr6W5wFoDfct2fvNWvNn5prERoxGkyvcAjRP7Cd7jvnWC5FPE6Dc/BR5TM5IHVRYxVi0tYZ9HGi1pE/hjiUUWxCojdWAmBEXY3ExVTUeYhrelb1PNpwTW3HSB0nkCdwwNNDKGgZJXo18KkRt2R/jlaBgcO2IXMGVfvTfVZkCx5CgWmwpRxY27bZuxiQgOb8IceOKL0WoLSYTmRhs4DcOQOygWjK/PEForihd0Ytp4lRK46H06PWUdM8RW3ziOXVbkuAqh+c7uuMOxogi7IQCYIatYH0Pm+fu+kHlD0Lgb24A3izYSNPWIwWPiGe9Y7J7ris/ZzgvFjVqyc/2SXWc2XAqNm8nWvkYreZsV9/ddJPgqVoSF/l+xAkJUi99P4L+WAz9gSP47NC4yVmr8Rlu5cHZ1R2EXAEQJ6H/XytlNTC5QUgE6Q+w/oRWp0SXb5ipXAaQ6rizoWtSA2r8IFobWwe/FYycFH9HuMhZTeGMUM2nH9IbHr/ljQDcKLGEIXtjUeSTHiRqJzI4sNIgE4Xp2gTM8vaqYE50UNHTAlett/rh0jIHpOeTPVvtYAfb6pUq47TdKPLBxs/AXwg0NVVWCDkMfyd+P8+wrsdJqjv9fB4+6r/PvBZd16Dq/5VI4nvocm9JJAXc4gVSdMM8cSk1TENUOHbfYzVFUOWh+XwFj+W/n2200vXl4rhqIDmKHgy2qu1zwkRQx3JJR8HeVloXD2aDrmLufPHKzhugd5NbEg0oGXaQhPm6PU9NLuOEl+dG+o+5aSgcHp551CB5ZR9y2X5i2QOmIeiHgn5wO3yq7DQlRvLDQafaS9UjbQrGAJUWzMd6/XwShVKxJgXy7apGydyvuz7XuT6eb2a1FX1NSLaUVPKylagayyrJMP8HgwPqdfXcLLDqbvP9aTHjEKPvAXkD/d9k5iRGV0ynvkkrdegFmVNiQcYiekOO7fFMUygOpQBCJBU+aaJU6+io8u9Gx3QQ73cn35EJJEf6d6rEQLGJ5u1zvRw8ZokcsjmnWitn/sJ/W90pGlcn18HmvkIaEtRUe0rzX/bcbm89/2C5cMR9MLylo3DTuJFRDjIiZcneDa15P3N94KUy1YQoGSyOzhm7Kqn5EAfnLbORjXdJkjvrIt1Osy6tB9b8mjC+sgCzAHtlsl3iB0EXH0ehLLTj0YcRJHnx88Z2vOdnp0fuUuaAquma64Y3ZWfrQme5WHCQgZUrttN65h+5ihZtenKR9jdzvRBdw56wibfWrAROp1y4hIuEnzRTEe6qC12FcstvXdMBcz0I7Sf8VyicJRcqPWtH2KnQOkFGzxLF/mqH7TotHTV+pj+hYiPD55ZxNuairlqaAeUqWT8IArAltTQltP7GCSyBDMP199nxwvmguLGqRx56vF0q6tfTgv5pdP0HqJCK1SG9uPWY+z2WC4wVDk2qyuRYc05+Iz4l7+VZD4nwXgHIhcuts8tejunEpSgWhKOQpa4xWnWmETKtTQ2sHU/M9EKZfmbYHG2ZKah+CQYyIpf5xtE4V69ubhOEm9pWEWRC3cZjDmffvO93ofp3c0JR/M1ShPgcrYsgAeKq+SH9bLhkA9V7nvy9h1HL2v3ViR7I1zdtI2E3uO/z+RJ0TTq3myLFOhfS2xJ2s3ICEf9SSiMFYBi8WKG0EE1N1JLeZ0Xn82IAafQc4MLI7Shvpr/LeYelWiDs/MO6PfOuAP1t9b6PGC/mJAvN+tU7Jwg5qyfVHPizXdnhElQqC7nt/SGWTLzJFtWJ2qNtywuhmD7KtlZJydKvSSHn5oYURywPOqD3UdyxBlTgNiHRF64z9h9NLyHoBM9XKx9rmNqRnJV3zebG1o6WR/C6w6LTFO2K8Uc6/tefmFmk/kO4TUujzCWBfWUPOXyYT5+w9ZFncADsm+SYb89fFE+uLBDXXYhMumRzh20N80VUEno9+9EUnz0dDnXTWvHWBlQzVUdD7cotd+stVmPugU5nncG9mO13Jo4BALQkJG4SsEkgER6aR/w4gccA40urgbbT6eAXsQrxitmc3lthxrLBHGOz1ImQZ6mScb1Ll+2wH/e1S26gxm6zwmWSKe7wCBVn1eHstDrgYeXGcn/unozcSnV/rHl1+Yfj22TBBjPu96ul8qAJ4Sx3092ZdvrVDLXZuQld/V7Pioi+ZYd75rzDo9T0J009hyeZMDwXPj8nVlaxVjYK2aWH98g8IkHxYt4Wv6q795alsgt3nycrvd98/Fnx/k8d6XrUs6CDnEUBWEO1UVsSKI0G+m/sd+o1pBsodfKKcJxVsMyVKbMjvyDhgyE2/3puXlX+z14XnRbrPMFmB/lcb9S29f0gBF5gX0egFKiv+abGguAC+yTyQOCvzqxSMTZild+oJn8s8Do5Vasqt+36VgdAhukkpEO762kwaRk64pa4jIy3ncgGCV+M0iZxXcfhisyA9DdbXrPRpD+gai3QOlgC0iYunub+O7EzxyyudC+Z+wBa6LeQ8kcE6M5pWrziCo7SdF4YETkJ5RZnd4WyZ/ebtX1T7XFF0BL1LtFIO4Z3U7LT6vlfDfbkSn9jY5ReLu6Gk9RCvCggJ8XPxvMqmuwP0ltKbsvvfkv5CPn+M62KI2PwiUz1YA/wyoHYAU7scY2Qr38chlQI1Ufy9K+n7Jm9jC25hbJkNSCL4W3QexKQEO2B5aLa1S+HOyaWfaLU8DoEJ7pj2I+g+TzAXryMjn9b2N+I05JyVEY48sUVvZkHVMiLp0P/UDuDqJvmP7r1qR69mUmPlaHR5Uz6/O0pULn+LdNl5Ccv38+v9bkxuPTWXr1e6TSz+fgGpbBLsmOyt5/luWp17MUv4NEUrT22+APFEmMjX90ZGN58AXp1HDc/ZnsmI1gAsZxdTaPXey6b5LMh4hX2ko2yuqRMvpWXJw4kwIZqSJnuiwLmbfrN7tfTkyq7oIdKcX0q1mRfOvtai9tdcJTLhUSuGzLsU7x0gkB8wXAqsuBLClSIDOsX8rn1PAK6A5MRnQ/kfSBdtV5GQb36E9iBLOhNRk+Z+NYFKx7WBxuy9VcCFWkCOLPwYObCLKo/+Ch32/LgcaFGRyNs+UUMPQRGS27V4UJfpSGqxwskooKPKLGgIav86YjSX34116q7gjTM501ObHBwjwUXoI+1ZQEAjR/GUOdiijsGKJF5niJX/dcrkAXJjb/m1mWRQJXBW+evBLT28EUh0GgTskfwOcBPFdYPCNi1DTLC9hTyY/2ShxiBttyoxQ8lNcMhVXRr0mz1rc9nVoBuY0Rpj5rL55TPbmlTQ6bhJ9faYSsw7NxtGlbJiGeLrjIPlhc/+Iwln+vc6JFws7gNRMDa0e+mbVg365fYa01m2jUFZoQQndLzQFPilQQ3jUyQQRIyaTyjSuvcd/RXaH0dNcpQyFkiTyNKD+aqjsiUsu5RwdxU5jrCICDMiwGH357yAj8UZZ7wB5t3zTd38ri8ElGvUYrbaK4VmDSJ0EosxVILhvyuikKB5zdA4ojC92AGGUvzNted5/ZutYgzLFU4ylmheI9RlqrAfa1vLTG75Zu4+JPp/JOlcbFqhu1ahu9u4fl49ytnMP4vlAcc0gj00lzhfh2wc2gtBzNwf8MDkXlSO1D2tAi5E4dp60Hf8j4nHsYjRD9jEfrGJJxJMAgW+vEgeeujBrvbGdZWWl2SaquJWhi0nN5STY5dehz4OyFvszAknMsqdkLz8aZqKImRUMFz7Anfrll75CrUkHAfAFH9YZP6bSh+EesTKqghsi13dUIaa9wnvmYLO5Lzh1mBagKtKU4mV+Lrz25c6MYMv8E5U1GXU1z2YvaSinKXakgYJmd+c22big09xxAwt2jbcNvJTAQ8Cgf3ssebzBr4tAolEdqVYTnulOxX1CvslYhjM8b2CvFLFd5OpL3D2Fso8QPkiwqP8sgqfKaiwRHezLkI8kebOtKLKFCPF7FrWgN+nPQBUUBdAXl/hjEoHerWMBGuj6suEfb5a931GyUtA2RYB7XBKh8srGAWckwwc1C0VOOElZu0Sz2tY0lPTxBgx5Xo3xMRh/gEfI3UNauPOlYmKOkfaqAFmDOa5THJhUDAswgr3zIJWt3j2X2oKxnIKjtCZcpU85zUTW+npoPrx42q/0LdHUGBo1TglDBt6VsZS+ajR1UciKER/ZXq3p8k1b/psnO5wrSsZ06vVwTKRqKEGiPlXv9CjJAAQTNNNJS9B2YhtGbIwDPxK/31SO6KjLaV/zDXGlOxOWLHj6IMelhUudH5rN9dFWkgQsw0aoEcgBjLOCS1FPZvDzRVQoA4gbnQcW6/NZChQWYR4ofoSFKJhMFv5yPXAvuUVYeijXhfSRODRddD8w1zBC2CkUeExuPTkX913BRpxHhXa3oV4qsmbr8dN84JRRCvn3foZFjEKgXMZV5WR3jVLJ6+lyfEuRyObrZOXcDGebcAgV+lwuqMHxrEbJjpv6hLxkua7Kl0uwLdQDuLl70IslpGsuFhDpvJtRYcEbIoJoJKFfcJr8CtVYIeS30wCyqSR3z4DVBF3LVb0QmtzuhQpgGw+/wgnexgtNuMCX1Za2YrmoP6b95Qr4uffCN/iSO/ZcEleoxq3v/Cy3wcoIK0izuqx092Pd1sdcOwScgPqjkuOWcky5ESRz8DV+H+YKIlWW3b6LeGjyeAKQJf6marQJDnUfxr6cWyEUe7i03lShwlsBcMEEMiTeg+ZqNnR6tuGzobzdw7dMdXA8cwQSQK/7De/5E5N1j2Gy/bdGU+tn2zBBZ/23mvKkCeo+l2z9bs/34ZoKEXrrxMs+59iQzrDURnN72zhHc0rJwBma55lXQuuIy8ocjoUOL0ALreM3qHEZB1ZLWfmG2WfMaDdYhJGBfMoLrPpnLcY7UvJhPuEfE2mQPRrBxw9LCkShZYeuJZxiWF2xbmDht0/z65A5IvC06JFcwMPvmRcIEBmXOWj2FJoPFmTv+TiVie4hx4E4MvRtZnJc4AHWSoY5CDJgYKkY5vIbgdjh162Z4hSVDHC0okcVP9fVLtb/RDux3LjybeAKdu2cdCsjHb1/IBTUEY+3Ag6wlmBWNPOoOorMbyXG/SEbWCRsq3n0sfJQA2y0pu+3b6fYuP0dJnrnBgIefK1sbFtEom0pIVDmRguqBer32CY+BYc2pBuEadcMwuscrUNDtNVuJ8vk77hWC5yABvfROFBW/gr1ACxzYx3HdXtKoMEbNSwSu1PtQ0j/Iv276UUOHVs+pjAKbkxcfjKeIU5ekz0tEvX1lJZrmqQvSOHbtgyZbUqeK/+tnPzLxcxnBrbnjnw1q9PSxKs08RKFEsnUouvrGRE8hnJ9yEdkqHLTNPTkn6R3vyPRsqk0trckOMXrcc+vjQEUVRc7PHkQhObZOjdR4Uohr8PDjfGpPQyquIG+HlNYdXAZ3SsGVZjuPgcfkOEkKq4vZUL5qUn8+USqVnPTu8mSy423+RVIlDIDp4vN7KjezkPL0MRmZWNI06MA5fJg/7vfVxYMRkI+Px4IiISY+1Fqfs+xwrzho5ORRQ/qi+wbGw97ZpDz7XoeaWln4SdpsSd8ky01PZNRnFH63Cl+wLTRKPMzt7sUv/WCJFMqBoJZf8ztKd4mTolPAbUSbeas3LUAEsFI3dRFW11Bu7S5m583ytbFFe1rTuSxdvPp2h12C5HO4rLvQAhtUs9PYDek31w6i40303Ic+7Df9qnzU/cPde7Uf3LeoFAUHvYj7RTkQ+dRriaLSoH9qfQ2OXmDewUk7TtVroNQBUfO6K3Y3LYoyiRfrx2GSYIxvI5QSyHdfYyV06aRcReIrSs54nfUJhfhgPPW4SZj0zEbczCymegIpgaeaztordgMvnTfWLa83SZjxhFZcYxWKu1HYF9Y2Hh4xtG5ebnxPV++uQ9s8Pn9i49UcqICx5B9a5AWnpHaeVs0k7Pr56628Xob65MlfJnj3MFdE1FjriLXovBLikxv0yPJnWWfl+hNLqKbkVRoN/D6jUu6zurnRV0VP4T60TNJbrsbTr78sDifZSkXfiWEm7h2lzZe3MOda3Zp+ISso18BECEf3IY2BDLSaGv0SDbhab826aG5oni53tmNlzj9SJKNlQYBNaymHfJjwjM+Ka5ZjpCG3lB1yhYAPd+WlP8DFYck8d+vvlOTg80MRgRjb590Y1jKyfuXIpPSRYnigh2jZ/qEYTFgL9m8OAkIDCLUPqlXCzoXzhVTnNRUIs2BikN3YkIMh9bAorjor8Atgm19ukQXYxoGSd7j0x7bjTpKG5z6YvMW8qzUWOX3PpqTnBmUqgDQWSWfjPhA88Q2gyc7Wv+XTSigi//0P1i50A8A6Er1NqYIqfT2YigdwYvnhzgHtzCzpL9Xif5ZrB40Pr4QI26+5CxGUqbVzYmTe3dqJmzaRDfDgNNubHfJq0rvDfZP5ws0sD+ORd7ukOL7wXSLT9bxscSe4eurkB68zesShc7+Z7IZo4m1nQfOCal3p7wavndCzC68ArUia2BTgvBoe2NTBY7ie+rxkEIuENvU/t4tjX0xnivj7t9PbYzy02Ea5GwrbcIbefDjhO55VK8lYrZqumyo6HNMoI5uq6WMLZ7gbCEWaB0YpiVxjt6e03JjpojY4Nc2pNTCGwdvnEaAix1DvVOUGlsZNPtNdxoPDUMgwGRtgPI62lFtEquzpkWh4wako5wOQCVKM13PUkEG+tAeq28gXqvIoRa/qCDMfV/KYO+keza7fE5r8O2+bGUJzXSv2byg9EnT/8x8FuLGa2pZQfrhQhVbrNvxjzQLUYBcslegQEtANqUU2BOTOHj0AKqjpLDaKRSnZa2F1Xh/rpN3/rooJcrLXPyCJPUoF39/pJrVE/2/KuLANcl9SIWC8GeUtNL3kTTxoIb+/XgKxWVBLaNNe9kCWqpFnsAVrGtHODVOou1lawv9WTHaQ4G2RJefSc4N87FhUlJb6ZoRf+yp+YvHeR2d3gd3CyHo6FnTPvQZWTkUuDhP9sQd+UMnKBHn4aM3k41OplVJTv24a0eFsZLBCsenaBC7YYsqEVeVzivnicJxpmYPXYuZyMJ46HsB2SVObIf+xDS8lR6sz+qMGClD7xUk7zEgUaOT7IBJs2ya/4qZANO8rZ9cmVT1uIvOQrBRTTA+P8ScggOIY1EmN3PtxMH0wKvnroCESasu8VSHnosWYy7NLm1B5gTxGRzqoLyBAwv+FUvLB20MTgY87v103m/zDLSdBxMjbYBrceJGFnOXE+Je0TEuuZl3o4G8b2Wo9jX+XMG7Q7clWazSQJkwjkyWL0UPem965UQ8HZ5htRCYwdg3Ix2kD9dhZzJwL8zPYmCoCmkUNK1Pz7PXlr3Hh3tQHLQczAlJCSbDmxFDEH342S7e4xPCtRJM3ICpFvECgONtPWKJPptr0gQ0FjkUNLil7FsUOoCr3tpJrFqx52OB3rAHs91JQczZehFdBq1WQS9MAXvPVo9qbt69GOQkjKx5Ws47jozbJ5CvAlh51NshzS6F+ruM179jMZZSlcb7JUP+2HYethCVSBj/ogP9jfLNGClR06Ku3ZqkCZIGsh2nHtlr5FbK+8q9Y97U3Or2mcuf6Dqw+Qq4pzGTbx0AM0no/92ZDDvOedj5c9LPYpW0+SBjp0974BSyoXm9XXxewUv4kL1JAJK8XSms9NhTGxc/YplAXz0iJVl5bunZNg1QgEP4zI2Nw8s4kNBamiyLXyAP64kodECBHSBad4/hNof+Iyjq4gQHXjCHIeHU1HFzcrefLpj2KAbs0NlipYA02QK4X58uR/HimlcfpYiIKS0CtFKx5W8Eh5WH7HT/Pq88WCkA21ryBCladEdDJ/nRxBFAwqCFdTdorooCYAJvtu0w3JRhkwKPPwk3yKCojT8UPHAVAkNVkQdNikK3OksicFh4Lgm/Xh5uhbtdwEGjLUIwPFTUXXyc9uemCHmleNL2m8/D+9FLKPeYT7E6wdP1kwVB0mxkF4B0PaRCOLeJka3U3UAxDuGdFXPhIOCXtS8Oj9kPcrMvGRw0/voyizoVS4yABk7DkOP3qLuYMP5IodZIswj1jMOyQ0yRnprJyiApOiOlYFfXLCrBQuhzcjnw77Q5vrR/qlGhE1kN/V2fjl8TnDJ3n+Hxy5H+tqryf7l0eVjV6WMgpVf0fT9ZYs0hU71ojJkAZLcV9zEdewEQQpIg/idF4tzZ13x9ftH4tngNZjSHd/JU/OZja3SxA01Ypz/U2QstAApg4k3Dj9jZ9N767sMjd1vT3cbdx8cmdiPRxue02KR54BV336SIOJrdpM7BrIhJJghkQgkPDqiI9T8LaIYkSc8vDmBY+SGXozwV5rtnHnuavV+sQuu8P3/7hItzOGbFfqGwVqmX02PYuDS4ThXjJtQWpEFeRy0wR2Y4gV5OXdt6nHiDvSLozKmrSM5wcFctWiep4qE0dPz0+lXPRfLyLlgPccmAmZ2G+l7ckhKEz2bmeyebo/IWK5Mbx+e7hjg1vRm00+5J3LaFa5G+aNwzyGwzbKmRxPYY6h+C6iHBPI8drl4cAPm5RvHtW16/NcZey62UcZjSyrITWCU1V5L4Lj/6CFiUuL85wwWZ7SX0m77G5SdjPOlcBBFLkFSvGJtp/FiYFqHACBg1T7J97uGwgGIzSxZDo1U+a0C6+Kta/AbvWhyO57dp+/ZQEgjCapTqqDOyKYXdcEjPHgOIwjCLxqJ0D5PVcZ4j3JoiARON6OTCQq/a9tiAAjv5dCVHljmH3/mQLkE5mYBJUWJlE2O/lTBvAxJU6qV7ONzwCL7omcJ8QuDX3KfnAjYvkQIdoWjmqnjxS9Nwokugs9IM6e2/R5jVANf3gobUvvxYBWZ9Q1NJVGQTu4siEidYFnF7zRTD9U2g8WP5qVeIZs91UJSufyEfhU+UAHPyc+33DkANITfHovGmYqZeXHRDfE2QFVInC3I6sSVFIqqCxIw9gugqsT/EnN8seYPTntKmc8rN9WZMEjJJDjVHlWNeBwMxrigU9yFZMsFF0v8H+IjxnT1ZHKQ8YTLp4KRAyPfJdtDQsWQEk3mocDqzX7jp7Hcj9DQcA9323BkBdImDRsDNj89r62v39YAHIK+ib/d4cbExJYOFg5ZibFD9gcTgd+r87v7/r+PimeOZmABfNrDsknXKEpXoKxmzkBE+GswB/Bqhzy9xAse+SdP/pqv12daXSuIxlplxZuknMXNXVFuz+gkvwv35Zw6LFEw96BgOJ7RbLlTH9DZ4wiJwxAU8w+bZ7FVOmhbjSoUgdAlWjv0pVvpRidYh1K+ATnCsIR2b51bgBgWwEulPKyujTzNXXEqv9dUNjS6XalitLnB2BIIpyyyX5WWpLuLQ/AK4paHRmMEzd4/iNLCP+HOAtEiURqh8WS+cIKOCuxBj/Wrh8Q5IrZ2d9RKFOPtbkJy15auNB57/lz3e2E6lle3ASTdxWrj1vSMEIeK8mwoxukjxz5H32DHzl2EejsWAgvvSbgqi590OoOgJDRfnlF+4pPsFRkU/v+oD3rky8bbT5od/yF9uTR24TPSRsDgcgHOG+Wo0j+pX74VyTX8L+pfIUSje1iJagik8lvQV3sHL+VuQ6sjYgqTPGSQYEs6MDIv/bBbqu+8Ky8qYmhjzw5XOXxUXY4Poc7VmD7gXO+g8qau6wDmH26VdVHaoz6qSlZHleUi/2ITRmNC5aEAk008Is7gaqZboKOZLmtHERK04i4UDJAH/pCP4oyaWU5XmYTUniykC59V0uINImhC8sLVtr2yBzSY4/OCygNG3XC1f9qXN806x7r6nsVmWoIsatIXtFxLlfbadiNHG+HXlKTcV7CP9r8JIPEMxzrbLT/gtj1PsKju6hPflgJzzPdc7r6GOn3ueBsnkRxOrnr3go7QSxnUUcrV35Cvrr2F1DX5YyNKqNjgVgUf//twesQlDO42RRKcDSmHInvt87Ud5roDH4Ta2FtGRgNZ42sR+R4Dw9hMDC5912KPBOXR9OekpotxJ0dOioTROAdW7zq53JNoiyLbKx09gz5vcXyCwTfNQgwA285VhNDUNxbake2mXHgrvH+Hh1BiaUIijE8n0nrBhUIkSFX960TWpDz57K8O4wOyqnWgGZ6lfo1ejmbDPzKuRFDagF87QjVwi6+wPTpya6nQHyuzV3FtZBRuVNOs7amHHnbE0oix7xrAf5paFyJit90ArgG/64usqHjs6RSfcSD0jWyS/A2d5xWZiB06MvsU+EAmOZC5sr1FDS0egL3kTr6Ek1cEnK3GadV0nCMAt3AuAk9iotvrmh/mk5wdtLI9uTYTWjLz3G8kqhf3an1V2oNaSzKNC5xBQr3UduKr1Cf+X0FJ0PO34Q+goH95XeoVkpHjoInEW8iYFvuAeeNC2H3OuPjz2q5O5cSMzh+xvLvGcbD/ZhhTHYWu322ANlwO+9zhSu7C66EYMCgVzDdmAVbvVBVVFN5/ZX5H54i/t+eczh6KWPoU7Tp7KpMtqqmHlofrvBogTQIBWUCJ7yfWdId2LqN+a8epsJttMBrBtAB5MTS+EA+MnsWffzEudH1umns84uqrxvEbk3tTfS0b/dMD6WsWAB0Z2SCEcK+0hZTmSzprWtHl2YlowqjFsh7c/HHQ01a0lDI5FD/I5Ryc9EsjT1++MQwEmrO8FZlp6X8nT9l0RO6ANha5d+AFjhFSFxi7PcKeARpgLdW602+Mpjvpnxmsau1R4Cix9ljLGXNora/kSByZyySjlKrOBEvzGbl20ivuLZzBbgvGAgJncvA036umGh2wHr7q2fCrJuk00DsbyGRfIuW2sPl0oCVngNqkIuId0BpHFogDjJo8TK3yqu3lwsf073xUxYPRKwmR0LzLBDLMwolzrG+gu77SdKjUdsmPcf6H04VO8/wiHZZLtnUqHkP/AECzOZNtCHazdNwXG7Su8M6shWs1/gHXvPHfaYCwITW0EBUWqUakNqRIMJQ1rdGaKcZ7RhJnBuJYK67+hyJNlEFlR5D6OP9e7DzREmaXI8qz1vgpMrTFWA7uUk0+HtLe9npYQw5LJm7GkmFK9ru41BhlhylvVemoQpT3tZR7rQMiKCkZI3t3dNWSEGKKRnL3iv2cVtWnqH9d/vNNIfUrkeJN33nXC7J062khk5hfhd1dWg4Gcf57SYS8uAMp13BNKKyXoQeZieWOkvUJgjfyGgZkh6HevFnTEXdlIw/Yg/Aa1ZYIT+SkH6wIvkKn6Jt3cIueBG7jiZ23iTUDbIFCe/K78EP1+HdjYl4YUW/DXoZqKbJsmrzHPq6RALnICDjYOKyEOpz8tKh+W95ay8hXjxc4loBU/dqZc0iJDStIulMmUJrRIr/twHvCFz7mLhSf2lscv532fKuHhBH6e8KcTWIpHD3xz4e4pVGYBzuK6jmB0GaUwKCvxq8630c3QcO+S71oPu3zcXwiuqt91CO7ugopyjtOlovF2GYYeSo2tQqzLT/iuOHiPLeH2LzVwk/50TZh+Io0oQIxSKMNfIE7bipQy0Z7td5H8Tni3ZxMbp8Tmxs6Vo1V5fQmzaO+6rTGlyMIpon5aJoozNsfBRQol4rLF+Htx9OId+rW3Fu3HQNhDwNe1z4Ax8jGc+0BO7speLbZCUZEIQzJZxqnjaltOhwaM56UKvomCoaR19X26mzNAIkaWhscUljxv2HJxlWsJeTZFWVr39v9JuxiKNJjC78JD/o/944vhLCXUdhAln5x2oo2Dw6Qe+EyykMlIzUxe5/3UqQKoNjKpismCLvczAE3l7mtALyIzsagEGMBuZkL6MXPP2DzLwbSlA6R1P/vBVuYg3SRCger0fuscfk6b5mmCsPIRI+SxlaaRetlaSCq0oMqbRKLd8kde+yaxPL44XXW4Tj0bQ00NtkYvna9FfrNm603awhdcgn+i8QBCezGyNR3/SwiT73PWqcMbSoWaz+HqsclLDaBH3uF4sv7Kr3Xc6eE2KCStv/6dYpM1VAznpTTSG8AdN7e9DIbHD0bym2j8CtS5736uEjCMouADIy7jc7VILhhEwFnIxuSKrB7p5dPaPG/2qrhxB6zc2j3DUtCTY+tW6gVP5keEYmKrbvsEFrYt09yLfcyo4i64ebXuDkkAbRmzxN5fB7urkP6ZkLv4CgQgy+Bz9sEGiFxqs2MkrHJXzjXIpQfLviY2sXrtQoch+CDPo2k0KsvDG8wXNGD/oUtuh5y5DSC9XEahEgD7tdTlfwhsNKQUmrAZVCxPDPFH0hFbT0dyuqXO2ZAjxbKD1QBAq4t52v305bq8IMTlmffmKTz9nDNqrwFB2q5qvTdmqgHEZn9U3AyBr6wZuxN+mkYJVxXo6v4B+VYtZkNascYeYttcRoJDY/IGQqYoEBEpf4hoCtkbF14p/xszqmRNcwwMFx3PTb8tkVcsXZY/n+FhKzBtp2zR3/TL0rgktjEmWnaJu7JGw+PwVjNCaJAXc5vMa8NtXKlJqtjOcabEYfu4jhDFe7nv1IgTW8NwSJ/NwfcMaoIMvT4G6+5KKkMFY+n6AgOrdApOJmLIWMChGxnFIETYrGiWKTByj+R2pyAxvpxm6joT1IwUom64KI8jGrxKLp1NtgXUTZXUEqqhtqIKB4cN92BHpUS+nscgbxf7rf+CrNv+H+UoCKFLXNQHaD3fIY70CbsG0VOrMhSXt5K//nae4T9vV3zliSE+z5h0dfWhA9qTEvx70lWvaIV6A4XooSE1nBMnCXrKHTmcknKOzWOPY6WCRxkri7a5vxXLqTdtJslQrQ6H8TbwsObTxU+QF+cPalgoYDL9GZYSdE2Jja1i4365HiXh1DmocIm8aJYqtnGr/SEA30393JkVI2jJ/kGNsjwSe+oIZIsWy0CcVQDCawD5wCpmhZLKnKfQg5bJtckxT9LVWsRI3H+v59F4EN9cJgGKhmURdYmbxNFJ6rHVTZjSr7U1AfXAaHZKTSFqIPBYmCwBR0PiTHWwALmozK9hhwmTEIOZ5/+Aa0tnVfF7QDmQojkkwqCcRNvKwkjAZ0TeMcEpGFVOESClvaW4MGDGFKr4iKGpPywZAKfux+UgPmm4VUWhxyegsMR/Jt7uk15Y0ZOygGnf/OIeWLeF/tH03qYLUPegrZgUDsmpq8trG8R8jXdxv2oc4uEqS07pmNjxFSaYN/bdovi4m1pHtHgErH9VW5DdCbEhq0g34zFL8u97vI1Oqmi5AzeHXYw/eRoV31K0SKUQKd/LaiQ6vnnWPqiWd9SP/Kwu3nNOZwMitKBmZ7Xd+oK6y2fLD6380gURyY+RtACCSoifFHPP3paa5s0gbMr7dk8UgqzoZPNO0UTnSUGoRnipTjajQi+TUW4S6H2CCfB3/N65+hRrA/qK9cFXholSvet1ZpHy2qZ3cJrpvkY2efr5/1WKKlh1VxI0Rq6xmqs0BnbcWOTXDNC4C4ihprkP9j9zilgn7VlUtPc635dXfMpqzmlzhkwfARfWMF0eEDmp48/JHJvPr4yuuXGydQWmWEBkR2fLOKBiUeXqZtu7jkeX83727cie+oas6xt2GfkpXWocV1TeVnf0AO6hMEVxJS4EAp7ZIJ4F2dxz6f+UlqqlQ0QBdidfLVgIf+22Qm1dj7sNfJhJ5xvxjfcVQl6YmkubRoSNxZQXXpnFPQBMOGcJj1VPO1ybjWtp3vRMhzIRO45x1SZLEbyRMPSTAdwUQ6pQvV4Hmd+3Aysh/bIQPAUkzE+ERfwxJPsAdVY8xVu8WCpD89NeepkTaTGGdYCEoe2N9Y/Syu0Dh20Qxb8vVzaQ+msiarsmUfU0UkKJK8ARBWr8zcKGl+BVJcGzZG3XhAC09BTUNGipEctdX74fCjOrQsiyZeWW/UUt0tXXZYqIBhJlts9G4IXn2OQZ26ZCjHUae5M8RvFFDrUWnzEz9D4InJ11APIDbMfUKywCXpf9jI0sEahQyg29pivS70bK1GzYyekdDFUtyu+hHzlnyvWj2snfU1HIvswY5fWDyHOEahoAwaKLNV2BO+VWDEgqlE3AT3g8bAbNjjEP9phswaYQlLs7t3M+fE69zi2kxrXWCV2cgvygO7sKa02rom8Zf7s6y56OvL2l8Ffwr95Q3j6P+3RxRMJK2nOHCJO7UvyS0mfxKirvrRu5lnDLSzLXLxGYcx7j60rXGVFcGvZ4ns0YDwio4a8QN8NiEVRxJvFAuZI6s2TDpBJU0paqAIIHxxxaQjJmErtFTqZV8y3rT3WRptudDJr8quv7y9yfK1SU/M2hFR9KWJlJyGX6UB1FCSuVxB13vkbM0RTKGnJmZsrY1ZHZlQ3cvCP+YcI2fFy5r+6oAuIjKVjL2mb4J7ViR5jR+88WK7/N1bC2EGsY1E1VbNNYlIdKjRLAYYk/bOpSscnopAt4/YtAKxwEBDFZuAc/g+T62to739fm/xAXj53QbT9R2n36EaZhQXTIfQXWgN13XJP/f4rFF2zEPBshITv1QdQiEQ29D6OAmPKF3AazPdaVvfleKB5AEMthcJU+3C1VhHmf4Mjg3BOzV3lKS7ImK6G3H68Ybi90VU80QRUTr2xvLuTuE//AVOUu4G0t6LCuryigK7+LSB7rgGjJ+9sUhjwhxwma9JkNkQ+Jlz9X9rv8fND7uDOpW/F+vwa3X/X7De/Xcp0V4B1HkBj54kZmzDWQi46VizsIYb7a3NGYkbeXEhIb9jo40wRiU6issvnq3HjLENQd5rOUtlvJYWiTeCSKSjnc786c6PZoZwuBQYI4vs2BwtyW9/o92e7tdOic2YsMAt7dTjVJnGmp0lxPbsQUc1TebcxBs13SR2Lb0cBt5RZsA8cmX2WKGZt3T9Aq3yLVspbDousy0TJQbUYJaW2YW1Yiink12+89yi0RAKcyjFc7QJrgdFmFCzOSb4NA+8nZ8vSwhA9UJQ/0FTv8UnbSWjkKK2Y8Yc57mAVWZyKoBXQaJOq1BAfTORs4KNMlE2H8lenEhlUzPQby5JqVl3mTF3um7xMGL0kJVjtAuiuQ2Tf4QEhYMbIXTRScnrRXRuXU5QDDGUhmXgtRqBczoQqGK60w1DbXZxKfwqB6W7JBHdctLBrcG5mzLG1G5FAT9f03++MP86+nHSQf63TCt+rssyQja471lSMzqRaZKMATIYbGLsL1p13jWhlrg+DljfXB97TfnCvmXZtV81FoPTrmahrSfZCPgVdc75wGgb4X7LHyCbq5pupl/X18PwS4bfLSk+tQrXfgidOoRECrAM5RtPI694BQRw+SZIJOfostT8OIjnAOLlQ+em04QBByqkPiC9+0uQEZeC2evkXT9I8OAo9Ei7xE7Ds+LSK0IY77zhpWmB7xEpk2qP3Z+tpZAkloOg73NyOO3tnyNzaNJoPT0jzMc4/awWQP3ajwdAdLo/OqHfKvS4l4w7EWkHbuM1B3i8KtEnlQ5rfsq6/E6oQAtTYWsWExfj/ksGw4oHoror2IGNNUGmpODTCt95+2Y57Q5kmhQTqyJGuk+JVeRgUkAM0xYkGBHRklW7exqCMK8nq63S7edy4SYBhUYSgA+GuyjqMz570iqxfNLMOByAF2zXYmgePB3ydhctVgp+FeHumCvfYbsztlkh+TGv6n+NbtjRecTUIlp+MExMElWfgGcd3QkiJGrIRbErpWOsUb4Ij1jSQaHm9jiutWixz2udTmBH7z/IpyltjH8bxGMTL/ynbe/DguvfT/I02c7f+uzF7ZiI7NLKR9O+wV2S5L1PpS7blL7uRPX5Uol8YGPVpx/1XlPxOwSCAEnnprrQ/U6XEjKz4O1FFkHqlL0g/CZ+Baro5lZFbjQbhadNrGoJtz9JmpilVihrLk1T8IaT9ZnRbdfA/B416gKtckLk2kXZAUGSXkMQU/v82tgjofurL95EtrAVGKqqFunencqDqCihRMgBx9XXiDXjuhWRh47vDTSWvppACbYfrvmzegbqilmw/OgOHf3rTJoug/e34A5GP0aYz5y013uCtFaKzcYsZsHtGYNDDwteckALWciQKtsJaeIAMjCVhHVHeIaBAwW4Reu38S9U2kG1P/mSVryB/uwa4ahQC8YJlLQgC9wIcTyLHhGQtHlBHMVPTQQVH4cROODS3go82Xd17h59NVUINQrFZ4dNXEYm/hRfOkZTxxnGKoEY+XAlc1iFdFXha/WXxR2s5bQb8J81Yxz/eyNS8mZW2n8H0wKuLlP/8ZR++k5szZR4dxh+6XqY8vhrcwcc6QQrWfmK2CvoCcDFOUG9JLY59b1f33Gfx4RQ/8Gr28mdbVLdviTihyXUYG4quUvdhN0cy4jGy+OisXu9On8vNhBv0QUzTZgR8s7Cii/DvlBz3gP2ZGMii45XzDh7vHax6sY8Rgu9Lu0CklxNxRlKbbS2eQCAcBooiu+cxOP4Biiz3KC76H7TuAM+lY+7+lBqsSzMY5LzeIMEcKs3mczKHusXsIHUqfV3HsUHZU51obWr1oedjL4dIhcqw+yblIbQaFMBD896JpoS+XA2zN+dxsUcxgkv+UnoU8ZpuPIpaJyJfPG1UmD+tZvh/fsXG3r+xDQXnYbzz96t5Ymj70sN0LrSsb5MGQAwQ+/RzJ0gcORWDJEYjUWhQwUBcybhTFYgZ7WmOxOqq/JmrBKKIa29nbrpd85j3uxQJ1q9PGTE57/ETWadcOszzlXEE9nXn3/ZtcI4jUfr3J7d8tsGPPjMi5dDrIdBjpgutuhaQaMPvDzha1DEx9c2h6Ty2xywXLhFXK4DY6HfthT+vtujyORUPX5EEYlj3NYXQfEJkxYNoSJqbXADPiGsnMy9Vnyw1z48LLQdTMSqUOqyshg91hfiA9QdMdWGXYTWxyZGQV9ZqF4DZW0yT6SzcyCelnaUrDeh64Igbgo5FyhH5D+UWQ3kz+gHzGhlAWZxYTFDoNWVMkFu985gJWdprJktCBsJfLCO4zYFkXuri0NRYqvg/r1010Obq5E5Dln6eIrFXGzHRdUT0XMf5lALB7wPTjPgTbLmAxB7QwsUpJMjOns0uaZIbZpWMvvO4dL5sGcQgo4XscxJbz265b8pp8LbtCnarp4NuTsXRESBdpdTs1YMApAluLDmZp4kmsUPqZJYnTJSjLRkciB2gTtuEYKA9vvphp4qCMVLbpJ5BpogoE6fmhQciFiIyBnd76rKYgjuviQUorqzi9kPL93PWD4lKkgTo/yJuH9sBVoAvoPvbA8+Hhorng9f5cmtgC0Vxsij7p2DCLDGrvMJ87t7GOBlrr8aps08LTwzTZixk7LuVat6m4I/HX0G4tFT69/FVcAlgsvtoM1ju4DC1pPrk4TzT9gMIoCGaJ9MOo7sjKDZIID5FMHNnRdsWO77WwdaiCt18sdqxxLLVCIZ77v3DLKeJif1M5Mosskobk/AOy7oTyxKb/cmwXmjv9LIIxbjettP8lcP6kv2BvYgLicoEaFCIwTLmqShIOhwY/cXlj8TKbj0wr4mV0DPZ+WWJVstCiIG/TsApl5QaitfYgXJ2+JLJADexXsct4KjDgCd18pQosRn9ozdjoUQmME8oebrHPKWJv4HQeoKtZ5tBYgATaeyud/WQ6ud1IvIqRN6n+UV0yKHcc8HD60GAprJBSecZRLh0xmO/luIdFNcgyJIfDuCjiOkPrVeuSrh9UTqUkaR4YZSie1Pz7krpOa+kI+h+dI7aM8dwfbeIF7/s1BRp+ksJ5VNJ0wiI7uVQr2UAEVk1h0n/dAObQL60aM/IfusN+OcpbVwEZJgEedUiIiN5aQBXip7fT1Cw7q7sjfQNERafGuBe/944nFHNxQayLGD9UgrutFL6ldJ6efTTvq2AovlQ7l++duuGA6aHRrCQvSD+GoMoV1YphQ5eGdACBJ5fT4LmziLxTxJZzTxV3EHqAPlQiawK7EM36xzYDmBGyA9aJtHNtRYyAo1TF2XbJx/7sbl1RdBQxeEsXNe7rlumHDZzRZb9OPL3BfskULfsp/xQflJxLzxZqn43zlOJc3XlqxeZRn7s0Nt6U/2ex0xawjQcy7E2uOh3/ahhjCtxp1Ouoqgs83tTjmsWlGEbQIEscKA5oH1utG0xNnx7dkNaobUBebaRtug869IVFoQ8Jaa9tpdPsLUKLGoTiNGr6XtfgVrODhyvdqr3fhhr1gJ05SdGOLv+dTL8rYbe9c4SFlHB6Em96D3wzdOfBrO/a4mHjJgKcfhzzGMJWBoQcOur5F41k/EdCm8pC70vcwYOYD552VLib9Qd59cHQCsBD64UB/VrCUI+hHhs0cNrvy8Heak2uuPoXrGYQdSX4ej4Ezlo7fUmVDszagNEmWK2+9mMLmUV8yzjqASdrhwVi8egfBn0R5+C7U0PDiFdEturkqZN5Q2hlOWnP7oVdaKIGmSnn3qG6GJRDz318aBk/jZ7Mj+VKyPjOueh6xL//NGbXfMF/SwyEzdrRE5pDAF6lThMyEhgCYHgD4LPreB3jXq4FGfQ7wkmO8xTERVhE9RLWT7zCZ+ajl0Fn07l57lwM33Pe17HbEGvfCK0q65zKa+EW+sKyEuFFHUgd/BEm2W5DAO0Bh1ExI5HLkw1JTXbN8rgLXYMZEONbZPgdNvCC70Z2T7PGqT5zUKOiiZ4YtyztjqKTjQed1bRyZ54ml9x9OdoGYJrc0fn4etNcEr/jw5zdAaocpvODtWhizDCC/ZxkiSqe+UdYsnb2vQbDT21n8UbbViiArc/AodHws4U6/ieEBfz6PBj7eFvc3FdQnayUVZTr1qwzclCwBVuBVkASqkuV+XhRdzGPLca517SN05COp8hQNi33nCUREk2EeVztUnGnzsXMNqTex6W2IaNlg/MgvQGc3rPjakkRGwC0K0jHJnLay7nP+3nxdGI6ccQGNHETwBGYSHKkvKX3g8cz+9rXedb2IODbVkOeUB58kBiDJoeCT4cAgXXenwX4oO+sWSFBwCr7Ebidv5k5xSG5JmfFuskPQirCkM2hEO1ibic1MY7gmp2BV8GvoK+QfKnyVs7LONmVH8cabNx9nw1vd1n9EqJgb0AqVwUAGWCzZh+/azVq2fhhEhbRVOp2Eau05hIPfp8Y/Xkl1vlPvwhbH2asJaAeuiF6mifOYcUGzpF6JuLEvFKx6LpE9lLjCs+SqZ+KcVDrDs5hU42HBLUUrcdUwhBj7L4/ZpKYL0CA/mEG0MK3XNeHIMa5BgthoRDpMUCzSHtYtD6ZjVuX2vgWFMI73aFqSdqMHLE+oS7ll0XImmZpkJprnoruUwJnEHZHQzqYsHL50UUV4Ls6YmlPsGBy34wWCPKjx7gH3Pz2a0lAEhr06HgD7sM2e0DpxUXkhNa0m7d81cCAytfyYnE0pAuF4eKGoT66wTTiRb7favei9N4fQZxwT5t1iWOSLrxYDGu78Newy/U2YFROqCVAEh9aycKNS14yiLh+RGTGzPjqm/6X/cqtnIOD6CUkzAniQ+V34fYuMG7C/XmoR0p/sVxAwHQuNTpy1QgYNoZsS9UN2o4JVpGBijTzKEIXi2Zma2Gon7Kd51IJ/elBEu3lZ5M3xm4Qc4Don8u9Gbkvh6Hqf6Z+AUO60waYiIPFPFa7goG+NE7l4/UGmuuDhH5XzVNOLeAXA/5EdgW51jLr3HTxYIntjKj2/7Bwfz384YIwYn/5cPmw9TdVmrhaiRj6DwtjJpQIpOk8TNLrr+6Tq033JytGxDGpGHHMOhUXK7tak0nSgDmxkpoW47dNzlX3rpX8KGPhfob4lVAW5linDEvi7bMGHCMyLt3axAfuTwjydmW6P9Yl3sezPv0skhF0mXli1m63beOISF5AZWzZERQR+ZaWnKR5i/ASQHwkVLGi2wPTN0S+PAcEA9zp8ykTpJnikJKaFQY0c58wbnOpqA56V0mRHuuyMWavJ32LsR+o0J7m0HsiOn3jRp3kPp8MMFL7Bs7btD0DxU73e1n6cJjY5lxEilNDjFsOFicZPqaml0tiFPxbNl+AGzq1Td1eQr6QwoEXyUUGjXjmUiFw0eYG5gJ3a/wDUvE1r85QFOwOcJmksBNyibQaAc4IcAlntn94qujgJ6eKSuxt1ODxyxycskFF7ZinFBolHWtjTyGAtiPbXOkiAtkMdyttB61z2bMdGaDCy4e9XPE4EhF6awhQwMNFToydrz/LFSUvMtSHINm+BYtSoyaprvHDNZlCOgqjW0qU6oyR9Rb5YSwA67UQVGePHwiXeomCp6QeU2WS5eU+LO0LafDn6b8DdhNMz9Ei8DWQXv/2fprGKGGgI5VMjsBAdYtQ7C9foZROJsOo1sHKJuLi/CGOjjH8L7CXqJNJhziKv4kt9OLLR5zyR5iqQjp6rFSLxbwLPQq8EiQ90qgZo8Wf9j0ANEBX3jDJZCw9seQpESPnC91GCsajJly6i0NMY4Jvmpi9pVmZJqZ/graIoNTwG8urLsYv7ZzZiwYsIBO4RvMoiKyETQ6Tc+QfUJHmF2NKLrRflODYyn5u1AzNwZCgG7LetaVXmlHejD6qGuB97bbqZEN458zPdsPHkna6+ZpO9JQ2W5OUInjwxqoTRCASyWPNrMLJyKBi8Nb3ilTEyBiAXj3B65k+AudZ/rVy6vD7fwMjkDm9E68KctpNh7hrO/qqZBg7zT8teaN1EGjfdsC5D+/sO6BvJ55fefJNFBjXm2XCFau4vt/hhnvnGqjjR38fBFqQvwqkP6Omn0yvFUtOtQCQeI0y+PBoqvHvwbflAttxwCA+qHRm/wphtCUrM7eIiKbpWYe27iNrDL1Z/1qDlYx3ci2tV/mGxAmLgIbOWXUHeW2gu267WUtc5NgJu3vNsAt0hNl2GYr884OVcjsMFDM0JfaXTLgR/9EfGM5ka3/wL364YdqtiqIcVSaoEbPJAaujlr8vPR9A6WtEdcm08Uhddme9Di9YgATe//P0r/60xRyEoKeggHq4z30B9rI8osidX37c/GWj1wveHXKMJon29t1KNnhwe/6skTXfuw+Pa/hq8rfBAp8cL4TqaKU7BZJoxuo9e2ypJIv2BSzbLKwixIGwAr8V0ciJik8fi5wGlAB84CX1DLc1wXgUMfN4hCR7CmKLm9IRYCrBQkvsgAjgDfmzwR611B01DcYZW07sUX1otOaIbApAX5PDHH4iA4OXsQlZzoTvCVU62Q7DSbQrL0pZsxZZRb5/oYkHwunKaBN6dmkEBMqku3FIzNqOPcEv5bUM4kdsd3hMJLI1SXN+6hJEBjXYBk1sNOTyUkfQ6VAQb530cFiLHEgNDvDHrOASdkadZrWiSmiVTUt36siOC8KKkYq/SSTFRU8XcIzui/WI9Ky5+Tw/4oH2LmjuT1OREXjS+2sRj/rQnuy0bmor58SCprep9JkyzzEOU2NHw/Cy9KTRX+tbaMrkin6hOHmq7KiNiOV0oM7BzA1T0Xiw5tCzbdLkGlfcSWvA4JiZvWrPl4fomn81/NRbLJFxeE79taVOMWucMZ+yl9iMk+cYDkAIm9t1DC7bUibNEv5nQUgrLE/ROgCe1QeFyn8IL5PCNHR81i9i54a2+i9QUbCliq2eBKKnLpw5q1wPd/xiGfAUVwfyRL2O1FRMu8zpPlgLSAM+7QtbXHFZg25lib43E88fhwXolbYTBsEIDSVYGSZ2RAmOuExZ1m0GPmaFm9tdnumvaMALMofJkcocsmbXEihG/NZnAdy7oZvsRntQUr74+fVXyaQ35ymPFp7pvvYOC4lUgq1h8G0L0S9Yhz70QsSZiy2/H3RtMpNWRLmBcumsjoGKrCS20zSnGJC7jJPGuGGCtIWKh0InsLUh++fvwvk1Iy/frLnGMP6+0B1NH1Kh6PLFKdc0x9ST/7aViRkhWXkcsPnWu0+uDcvgFjCeFL+P0vzyi+OLOn3DujK/CTkuYgi9K4MXn58x6+PT0J78Gkkgrr6Ql9ds012vbXDaJmF5qic7NNqKkf14SpdI5npMqUiPbULRcxBS2gCx1sALNrj+nMzEKFiW/xZYQ4vruKnrcDJzdGRdlMpp7FMOGyB0WfKc40WIztqVE9ArVpf6LG+Dxa/ied+G1hkLaV1szXwsG1atenOBhvXhbb21IWV//c83EZ+FTzA8L8VqMjFkT6WTlkG+TZAI5nT4kItozX/yavpT1O9D8G+d+1T7Uv9y4Tmngm2ChPMbZzEyHMmb31o1Ygk7NDp0l3P51tqOyMF6DYltMg1/mWBv0UlitoIue8w1T/AlPTpM6Rv1+Wyjl/FXbdpMKKVQh/7lrQXtr84UCvJbBChUOeaZ7rRcIIC6scCd3Bx03ahYdIyKaRnjSqvv7UH6ZRjBMXRgEGe7d9ZlNWNsT8/lrm37VXJO/KiBcYt3Qh/d52BXsh+feZ6kZGjnIYo0JZ5pSjSALFL04ELbMX24bhDhMJcTZ0t6fH2zsX9Caol2WvaYjx46zRaO03/bbTumKNJ1+9UlKA7FdAb87OPjHs5wE1NPbqWh3PD4wlphoVEWT3BrZZf7rWtus+7Zxx04B7E479i2iTNUNCZnGxaVsVgnT1/3vDLofC7nYJMOgi0crXDxH3ahTFlOtNd0Rpr9ptOp9UHg7sS5xAuG+5zzBj61RJHzt6iy4HrBd7tMu31AYqW60RGARLrhMAmdw8JIfs4MYDLFTz599JO0rieq8oitbZkUIZTJrwKG31kmXRO1NRbuAukVkERnMfXr6CLwyQzazurMdAvXEFIJs6SEtt5dycxQrhz2e9K1pc4vCM0tPzwIjXiBgNwNnrQqlt/qWR8z5ougRVgsTSpc0sQvZx3af8GGYTRjbz2L3Oqd+ZvuyanUmSTR0hr1Cm/LmyITKfKhR52cH7ULLX3qrbsN59/0N9JWd3DPlXdIHfdJjQWP3U5tO8v20FYIDPSSzI50pCwQ8X+Rw5GJK29YlikZFYklOZW43R+hrf/I2kJS5Axwxpew56a/FAI303WIi7NTWlFtslvgedohD/E1DlpnOHF2SFR6AWiu83eMMjhYppqvhAIfID11VKdPnPBcudU1fkRPbk8UWm/SenAowL7zVp9s+XRWz/Q9RwwpM919uvj3IBRRv6EgDhCa5yFHe0zWWILgxuHs8y19fX2YhsA4pwm2Ez3K9cCQ/0idoxsF4gaXMoLB0NOb2PonyZerh2wLlhlwbhADSNmge7EFUPLf5eViimwZjJz490PSTy/6WBezl/m1zc88s1RN1y3U8eUEHQemEK2J80gR5agqwhxO4rM8s/19m0AO43ftv6k4Jx/Qk1OpE6qdjBURciRT0i3F23lodfTnOwc8xGEBrMDcjV/NMMQZXPyvGaHVPGQP5mrp8lJz65GXnu0RVRkxo1ciu7i/THfWd5RD+0mk0aasHcn0pLzWIlmwRlDYFMaKmhaui++K9Comcsw2blbVF4YiXkwpF1Vtd8HVlzilzA/AZLGySqm35a6hU/GUMdgo4Ncrzup1Gr1+FMZgAisr/Flz8VsGtCmKwM/wFKDSijD8jKSHVxKAea0ZEMUiR3HhcNlNadvZ7dZrX1RHqSnL2hvydBLT7H2fRS4JxHHri+bjmFk3iDRDSwvvViZ0Wn9UjrBOXSUrn29pxjucJ4YUX7EKzO7jyRfwuf2kO0vYd/JcIoA7X+V5Lsda/8cuFMWFXrM41VXQz92ggazMeQmO4IBR1s5ZZI36lI34xiJkzQR+DOWsu+2t0lq4v/pFgCAsy0kHyr854ZYGETgbrmsmPOJElcqZ4Quw3aD+qWOMJ5s2McSDWfqlYVSHWSlipPsxG+1WaOi081WJ8Iz5GqnEaO4o1IAogh9QmVN4mHIV7eoXJWgtAwNXqkkXEh7ukn5KqrrCgD8Xbsq1CkGKpzKu5vna/GbWrbY2WHrY906M9KBE+wvMMOuyZgX7IcaswvsUrtP/Ho11Ey7D1EioHA0BgTpfWrK5uE9smwWacTsEMqHsm7RQJTriM2xhDUNkFZjr+C4mr2aX1rPw1lZq/Wdu9lHwN03I79LqdpTv2Bpr//5GTa2vG7J/FXvyFi6f3jYl37R6s2DigCw+qsJs6rYkaURHx/Jg+d+nD+76ckRFHv7p4MRQ+zVFa1KDgJ7D5AwycIw1EKm+YtpVab+3iZnj2gX6XYyrGXKrK5wHksdjx54uJIqgEIxe/RC1bLj5LcHTOh7JbqroTIYtyp/1fIuy/TPsJcrb5xWizdGXPy/WWShvev1/Z77DONNwbWpJp7zEnaHscjhLjaLAHbocauOW7RrH24T6SJpE3Pn9+3jHzxYUD6QpIVLHU0lcCGhgjZ7NTbumsfFrFxyO+IPhVXVunszh+I9fYvV3ij+GqrYBm/jFkv7YdVUDf2VKlosXXaw282YBGNmxZynP6CH3NO3vk4NF3G0X6jSsk8gPa0h7ZeDJsOb+uPymjoCBOjaqQEbjzHJuBg4/8rvCQVlscrgUUj3L4nOqiwCPY5ay2y3XunxSK3N5iaxzkxwFzC2DfiuU7dFKew5qFxPEKa7BX1zKKOJ/CuhetqFhYq8eNG8XWYmD/VimlefPZ1LpiL7iEzOWXrhbpwVNOImQLAFaZUYLZgebim1eavtndL8+qc3qAy+dx/RS/LPZbfSpvtRqJTRmpkSVs6ArqT6m/lYOJ/w0RTSiQQT/WIJwj//HX91zI9g01tS7QJ/IGpUlxIqgrWWkf15CBEixzBDS2SvfWoH7p8bCSwCdelTE3ay9hRNvZgmlcn9yJxrFyENDiNajZn/M/QeWYiG8uYnB1HcHZvU7d6eFkn80jzQcjBAeLasrdq7jh4GB4HJIFLRQMxhBSlVJbJhai/7J4t2JpAL4JV2C0ZDo56unNPQtEf0kO2ZGmfd/AH5WDO6QVnugi02/9jacreDRTs8iIphITUCJT4zQjc75p5qaZNyrfCHnTpAh5I+NnQk5JLdb28zOV0OD0xY1l1I8uUI9wG7+kiXbCq8hjOiA5TiylHSytU4nwdoRaoE9cxRoNGlHRyJX1oJPgL7EJAUOGllv4YQxhTdlBzn+GvnLMzdcVPo98AKCW2qfkaLZaJiC3KEIFjdb8tYmwmpQd18/rq0UH6Vjn9CmdggHGaOyohS4+bZYbswPmdzj7fF/1ziyXs1XWgduV9+FNYGS4RWZNEgPff8XV56wSkh1Z+8jJ3jViJZwVr2QLpM9RurGZbsL42gw/9nWqgNR03hpTDbEUcSDvusk83L2nsSIhDOwraqIlnoIFqomO7Wwh5RKZSVBw2bT0ab3UZPLwPbDQLNzYD8BbbyOZ74J/DDWOKDRrsMvUdjHJsp+eCFCo75CAnH1TzQ/2ndLWi4NKOFDWgPbfaLjScLwiZbnFWIrMLC2UUgkuqBvSmmIsKaYsmi3XzVMdqiLhSaIOV6GvBgIITQdBNNfeo0vbYJnoz3fKmoGQHWjBamtu4d31ZQAHRBDC58VZdeO5hS1NTC6PM+ozN+1wZWr759JJorcF9yWWGQpfaJJhNlPMPBNDO+z9i8+88c8J/MXqHVhRYmRXQoO1kDGjQWtFr5kTkAbLBL5A1l6WLl86hEe1wqN6uQoTFXYC73KBorl1PalaaEVXVWs1Ko91WnoaFlMTKvNf9A0HjMI9x5ivYAM3P2h//VtPz71XD4LsN8WS9kSo29buZuvNMLsd5VNwBxc93U43tKKJ9ygy8ENWqCArmXLdxpH1oPKbKlVppkW6F4pPXU8LdRM6GhN3Fvc7inmLwgA3k3m1S66TvnTot30KmEVS9kGXI7LL+9vh2Iejd8pjGYzMr8ZUvBiOpMTaPCSs74uc8R2e4i53Sn63JkhowxwQxty5ryAAySmZsXEEGFE7D6hp6KyD1WqXO0vn1vcio05I31DNNI3dqj+F/UPWlXeBVczSjg3sPLMVY8NDAbe3vgm1RQmxVB5xiB3Ho7NWgQvdC008zGpEx86kbo588RnbpUdyWgxCMU+6l1DP9F789wt9urPRNSIhwUT7a69dr4vJBa3IYsqaHlu1iEI2nOOJzdqHl7wi+3KZf+tTgEa4MZHnJmG52ErTAnCRpDcdP8IPBySZiqPxqSKVjkgIqYHjRcD1xvgNXUnYBXDepMuvdvOae/8gA2lMNgsq962ntm9A3qXtAxjsIfkpgFif1f2nSDU9uKPyqTW/qiA88JGNEXvv9GwvXsMT91y/Cf3J92ukNBJ2zH0SObZqW2x+nKtdXqQJJdxPSFCuGdXM8hxmb1/zYk9m8mUndkHB4V0+gvfhR8MV2KWBYaduWDFk65q8dwJigMLLAX+VoBzrVDldOPdquWjbKvqNLKkYWcFYCTWG4UlQHlkp+F5wy9Yk/6/DOe36/nb4v6tz0DrUGSNA1hK8eJ+o0hGP7HvIYPdapHOgZbEB40UWoTIvjB+Ly414A4uUE+UHeaSy99do6gC16ofATh8Xvv1efjyfE2x2tYGECWnEitXLXk/HJgUkLL3RVwLzHmO+YcB0nK+Gn7q9V8EPpjYmdfaWSN4PmkHRJqu5m/WXNqbebzlj90ioudMS93YKw6iPjmeCQ/WgKd5E2CijhnVAGj+lXth463ohj2MF4cACMZcQCKTumRvBlYd8LZ7aFD5MtItuI5bifWKK92LUqrdJCIQk81kRgvFwX2afQWdkRT15FfxsSecwuR41UYULQG+P+mCasNkeXkYmjeVhYnU9bA0C91icGnTrkNrzF+ZrRKnxj9RCe6xCkAg6FTpFIBeMq3dTWbYy38emkugAeS03e3wrvNWgPY1dlzMLHYy3g+PG5o/2QwYeCdpWanGL0bxwt3BAQljKamVwcJBr7HIat90sKMS4Iqse4FzcxZijrx9ZNqCk0WmV4KW2hRuZZPFYfTb7fFyhaoMkW/9hbzABZApS07rCYnNZhQ0j5PRojZ8kpcmb6CrVLiZP8iuSI84H4L3HP23/kLyhCzTm6URHMfAa61YNt+sKMCPXCpnyj4PthbkSxTIwTiQ+nvJl8M2dQiuU+ULNAS7+OP98L1FN66tkwaIFQFQl7Rc7elg22PNKOKZOCPyhTXkusjetIhMJUsZ2LqOytCTAP+F9JsTbL+0Gw0skfiOC5VB50RQdgyh9eIxQfDF38PGNGgHJUmg0t7Bon8Z/wA8EySutHCTxR80IqwtPtkgbwve+Luh+wlyKJ5Bj4yTCTYDWd+Qm3UZOAqkwI3K3SE4NXfksu9RO+gmi/hUAH+M3JSMVLRhdadn0Zd9+gCvkhAYY0RQhEBTk3QkWNBSSIPaUQo9b0ktPGntiBC2OTzhyH/lpNxcscu7VxrRj5fPG4xlHTQ3P/Zzpns2Sh/f1A2Wn/46yXHifpIruHKWICKKhaI0boHFDKui/HzhJzf3PAhMJufpVk/r/G3IWs5uz5TN6nPf6JJg3aJoJTHSal/nYQwckhQ9+mHbGaDZxjJuZnKPbnHtyR54rUvzkFGLdK+01T0/xmRWn1jNuQQcq1NR8okQgv35phzoLJZrAhGWFf45FQYNNHbiZxQMmrmItnZqGIGps5PyPGykNagTfCgOWYlyakorTvA4ekAGpWfkrWJSgdvQMA9dsbup4QB2acXBANul6xbCKpudtP/3anNX95EglOhmDkSgsDB/J+Oh4RjH7ygImRxaIsr9xFM8DNWkacMZG8e5Nk9r6OJsaK6jvSmT4YyksBlqB4R21M+DAgXUz8qqgFsrf4Cv6ui/ahC0+3ZOch8BRuYlJNpkHhXAIstHwQ5ouJK1Yt+YX2Fa9s+mO/lOHDCNb2PRMTLj+Dxx3ewTvKvY2cKWB/jqfF19Ewe64DahCHehBxN10KMz6VZpJ/ZsC9tm3U41L4Hy3i9CCty96ruMHmDZwdxxG5J4V0EYE6EM6oes5ALhjDdD0CM2Ge9Oi3MUNHP8m65Vfh8jq/nZhmMiur4w9iPesen03EyTsetcv6PAguMbevuVw7iOFiVecqUCGZ5gPXmcXo4ub6Mf9MM6LbveN0GwQ1pfbyO9u0RyeGEV97pHQQ3Aui2B8RzblM3dJ1/D9cgKUzYrIEieSJL5yOThGb95Y8nuemq6ZocXKJcDKlyuHygXO2zqdul5Fm0Y8BIHV7W55EJ+GSTfQ2pZj4e5ZOAE1Rvb3hVIm12qXoxbpcThY30J3Ey3duM1jpBs+mHHB9/tQ0xZ1jBiwrZPjVBtltWhmjkEGcIsQgc5JRO819yW8FQR8+OSjyclYHbVFRTqNtKTCf3VqTkSz/LskAF3/W5UrGu6TJ8zTY6BHphahu5SMverOEz1T4T+1stHB1SbdU6rVkrWc35jpN8kP7xOCW//Y/tPtfFy/Rnttr97hJTNrbf+dEOh4xPqayrMF+OxbIGQCqoPHfPV4wGysn9RYHhWlvbSE7hwMjo87Wi524Tljcof8XJI682+NAZ8iLJvDB4RZOTb68rbPjcku9ovKeqwhHQ3ojDTySQ/Ct7w2qWEoK29bk/NiBQmZ2XJc/npTPm+jnDNjyJ/BQiTiQ+JWFWTlbTmPSBPVEr1Jk+zsOMXWM9vm4bS+hNaxnKuM18itHRaJARXHOW6qv6e63Jct5S6yG6oVxWsCAnDxB3T5z67Xtym8KadBYX1gUW9XAKiVjjpoHzj5b3xKQWQpwNbl95ajR3h+/qTJIFHMAYFTHp8myMiCDHDwE3KplpC/DYyzoKDeDZc+859RiISn0H0O5ZLs7cgthPyNd5+kIbKjchXlRC2YDVUOcK93P8gwhVTfMu9nPBlSBZUVqZbe4l2DGV8a8ga61haj0qH42AkRphVN3zg3vzr/JmmYtBvpYd3VGeRtQOVicLramQgxnz+FLRivcfA8J5v6AzeBBSafd5g7iU9+e2amFmZa15mU4IJdSTHvFINQiBDKu5Bg6BM5a+lU2/QnvW1i7BOnt9qdwKuNctRFY3+/mrIDOTtS1K2w2Ycs0grCdHW9Ho3PhAQKJFmrcy/v7gbVp3DoXn2Ja9RfO+S4z+OTdlkAtoPsVCYtJ3+RP1wjTX+xuAkzb3iV3taahm/849PZI7+goCvxzuqjShN7CeXTIcqvmD51PMfdhcmedW2VEpU/RfFNtckdcAICenIspenj+J6gvbdL6/Fm63UufhTjkGnHE1yH7Av8BJyLBxZM5erA5XQ7d0wLbuuo/X2KK5ABzhOsb1Ko25ExpViTT0KwcDtSP0cd1esr5fXi9uJHRq4QOeWldedKGH/mEsN2ZYwJkrCGyeqfKM4uEplog6i3V2JAp1T/yHUFMYuGbAH3eqTEcZKEQ9MM+KKY/uA5mgNHvdWhXnb2TwYgwDl3wGxWYJKDHpSpkg8MhvQYGm0CJ2rnXNRYUV+WPGq5wzgnYt4PAofkk4NJ+FcEu7I1hPbwR6Y/TFApSdV+X/9+wNNbllr6OD55E/IvVbUL5zG9ZqDbae67L/iQsq4soZJl8yzcrw/B7N0SCtnGUelt28pd8CAVTzFU4LCAezFe97eCD1+0sW5oPbL5/d0L9EQu03azAo7+EulHdfNmBEWdVaP28rxHFdfCTHwTjGGNIozKXNcuS99KEDoB0fvR4eZB0dXmUxXOpPUQOiY2G5Sko16Dx/0J55521G/aYqI5P+d0goeV+GFQJplVqwPEjWNyJV5G8/DL0Na61rgTG84aScLwEc2nelJNazHNsevm0fMQoOyST1CO9B1iOsP6UcydNEPcH/xDBraEvqWL3A/M0ZAqe4uaJ0juiq0AlwLWCe5vHTDBdAM5yAZEZMY4BYSAnKM88HFR3kGYomJMphCCDQLct0eZkCO4s9TG5q4cH35/mx7RJsrQLnCku15wfF3jwcVCa+F3CHLTb6Cik41hvyebHBUtyf3kZKL9pIuG32yUhNnrOqeMZLLIBt7RgjWRwL1aqXG6YgxFCVDQ/5DPB8ZVC081fb+wstcRqUDwDfjxmgGND68CulnR/2f2iriogLgLb3NkkJjJq/pasPoXjoN+JHm1Q8L7aUVrKkUugRvaj7TJ3NlwBrjDKnjv4aYq6+AHHneTOH4h/3KZJS9lmEYAkCKRP3a3pVtoqG77ogHuopdL1YVdhxM554GrJ1FYojxIbeqv4nxOZg2P8x0KdqWEy6WV6xYUbT8bDe2aMNfa91NxHL77o2QDowroAq79JBP1+vhFR+7jW6D2AjBxrk4YZeoh53hG0tgRSYqqG6LtSqn+LTcPXY404iWi/vmqAheunS0ck4/N539s7eQWDdcW4TFL3A6yI+Nhgf7KJYqUSva3mAmghwiSqEkJ2Wiu9Wh9Nsdfbfht4NjFSmDkSosJ3j6RgDV8xOAojkfS4DJlzquVInTm9EJ1Vclq5e6ZVwIqNMTwhgczTV2HkouRHWRyq6r8wIePmd77MxJGiCLuDCMRm5tdDNGUCmiBzhAXgctFw7Y/o/+7bZKGusB9dYOZFpT4hnjfklNhw7BxMSyrcTyvTluhkbNMqv5JBMgOIG8oPR7aJih30oI2MLeZ17gB3MAEBW4IMhiQFEaY+7nnWePf+ofcZHJ2VRVk62OEpLbC+CClGu45A6/WeiAlt3XB6eifcV6IzXOn+S9ADeEv7C4qXNBbllD6QkWlzg4WQZgSqgSqy0ofb5bD6l43Lxi65ISst8LK13wTiwqwmhpHsQi2eo92f3QFfEeoCp/NGWNJPDxqmQ8xgt5l2N995j9Ydo8FknTrHTiTcs5oeOLdv33m7G0AlF/p+PSR56fSJzDp7I+bMaW5K7eRmlBzi8e42mVs4nJlIBVQIHvKIUBVj204jhXU9s8NiWtEBLRgeF1Oj3atVjKW6HyhVuBdglWrBgAmVB7d4KI+xh4tTJdQuC/rzugMCnzISji057nFxE9+fULQKhGPmINms2UNfPHwax5M0qxhMF4dAF8tXolhRJBitT/7xoxhbCyPEt0LHEOqCvg38mdiTH9KfzrhQCxE3KAz9Tvc0W+fCVZ117IyDMS0cbkc66sBwRMFbaWGRAVzO+/d1APL9+CZv2oabJrD2QD0nXAS0pI/4P1yZGVnI7FEhEZ9xSgkXm9sD2PYuTodQEVZeGqOMk6y5p0zPHDCZ+X/Ws7O/2o7P2Z7WFgkpF7Z05kQGxTkURTPcu05wMX3VyedcU/odfpDR2NsV0JpgmqxXNDth3zSNNarGUQbrS9+rfp7dc0J9ar0Sa6MxM2a7NSwNXCVi9KtTCmqWALhKhS3a+fJy9HARCroe/rTSMXT4ndo+txQXCXfLEGLJ05xWo58qGuIbd1XWbiADDPjMr1glzx9OZ3LXFf0lDdg6UsW7lAwflN8aD+dUP+9N4eSunodTOFS6AOqvEJcyqL90mVBQMW2CwdbxhMg1pdRHNY/uIbZA4UE90un4LDYB22ejAE30DlAR9x8tTr8uosgkKM6FmlinCto1QUpui4IHcrkrEWcma4eKcNTLQgxPlUfsN7zIaonF01wEQ8UJtADKxelsQUyn9oX4RtIEkN+TwFD/8pazJlbVk/HaxM2bXuTPpgSJA/7vn+maRSzk7WIX65YbtNutD/QF9BEr0+whzrK8LCnjMTtdbzhNPrEib7/3H1uZisixF3qMt4bphRKPOQwxwB8f6aZiJLiIbaaw+gKI4F2OIlbiBrsq5agfaBuq0SY08jSwt6dY0dUr8mxfWJt4HvJsr2Ojc/1kfOSMoSuJcMNNLAHJPaUk2gVYqKQNDFcrhbEdrhMuC6NXbUqwLjv7RVbi3uElqmkdVJDznN9+a84NKYxKlqnQvrPNvfoeza/I+tIZpNYWkTRRcm7hi2Opqf4lEa6bXFoe2eXLkIU4xVcPLZP/nT6KprxfZN2SVGRydTuNLBHf9Ratcp/GhCXhJVWdx+506wt8JbO5LPPulU0U2LJQ2X5QZPrNrF2PQzpNrZcCVou88txGEY6zydZQCKQzmr/TB3MaE+L+yDFWc/lBDFcmDmGjwto5M0W42O6W0q0AGSR1SPmIlUko1NAdDVRpQNuqtf29eh3qKAcLfcQDsgyNQ6Jhg29nkgI3TQhwgL0e3Z9eFoZwpd2NNcKeQ36e6GrQ/9HhaaubnelzxIy2jGuL5dwWVFNvp/lyVtlreRoaBKHrc0EpBme/bZSGzT7AlKoTmNR93cq2gj3gteKqoA1uSjF8Z0gQR0tdXlSRSAa++4SvFAx2mXDECN52qxZkfYtAkrSMyzsgxCmCuD7HBIkixVOw30PQ5UCTtyXxGO0Ze9GcOQC+x4fON8RQpajZN1DTG1TtQ5FJJBBOWJbVeanmjUDVUFFhWs7z4NmqYgkii9/ir3HvuL+gG3qSNyIGTIw3RetYslBEETp/SzVN8uGeJm+tdmaCd71F3sbEByVgeiTYz04OSSb7Hom1r/1IM3PSCGye1e7hwbRUMyEM1HV33coNJTI1bbTltM6LYNDygWe9wZzUZpCCvSptZdQjEzw5nLC9KZ+NNLVDEfYnOqUdZU217Ts/qffEZedHA9a6oLZxevZvvfkPpHK/YSDCxIZEkIPsXZ9CwfxuLzjN70WY3pOAU3+LCDwcBe3X9c6Nsweo5VGJAv9g+RMwLuRHbe18uoEPmztnc7CkXKpdU3O5yLUSSpK21MqOonmToUBDCUs2bC8ss2zPu7bIZ6ZoJWJ2Ca4RvgwBrbMVXWWgDDjPgwMWBMMQ36sYuH+DcVgEoGcYfD9AemsmWQ5kbASYgxe+PIcY/aGmnE4om2wvXKorJbPFj8+wvbPpqYYnMxrrC5kORPawu5cDdJ1UVlckHT/MW91NO9N8va19G3D0rJESCvAQCsnANe6TXlScjppDrnM+71/yL75GrjYEL8mrtvP1xQreXr2CUJvRlPQmmrAWF+t9R/GVzaBJHywhTKGgi6uRsX6ymI/knpT2N8eGDXVlWy3Pvie9Mi35wRvFot69Dv06wbjUgsuhNvTmbYEJxV7cn/HMZjTtjA9AIbHgAsp6d9j36VxIg/qNQlPXze2z4AtVwHJyyMldW9ZzibsliCNMnxu7EN0mNBA4axw2BblQo+UmAuA67o2VzF5Ahdrdl1UXgOr0nGGB/mI2H3V9yCtZyH65MB6dIcFlEGWSygRauqzrdCncYhjgH+M6Y0oR3drgK5FNP7PSjiRgnnp26Yl9QaY4eLKjtNl7Ksg/Kj1fWNquKQISGiwD40DvmFyEQk6MPbiuWdC8l3Mc8XPbV3XCnE8CPZ7i59eJxTwEL4qf71R7sSSjdOZa8hF4kp93hxAc6aTwgtYRbjPrbsUJe0WIl6jZGbu3rtae2nniUWcU3mXZRhywDECvnn1Wr/fHwd41qF+KFHCZJfjaMerj0RhlmKQiwHuJe1ob+psGKRp7cn872IpvjFnJzI463xuaN2UpBUwp/e2E2Q8dN6PBTy0OOPpsNXlKMELlMO/qmQzE/aiLbWKcI1131QSHH1NfH3RJqwWBug3AJsQ/e7clR6w0lrlqhFM6RQXggUA/Pl37271WtAnWpnrAyTgQmT2PzOaF3Ft3uMNZIi/aESavP9dQPqGuHp3T0LrYFSYt5ifBG7RWJ9X+poMNn9xkTIZeV9NellWKaZjatCUzEoWFnSMz3Y4qEpOCLPyyzXw87mqh9Gore5mkbRCu0NHvcGq9jvhHSrNOLJ9HlR1WrL1bJTZ5xnxFZwizDD672SJvwC8P4gmLO6tzydiSjt/IBtueeQawLmPq//fZds8ZONMYF4WgDbBBq4DfUlGSUb7y2q+6BA/Etd3bxyx8kVQXAAdetA9tPYpTtLvG/fIJhTIz0rWnTxQbon6KO/4/x2RxttEznL+8MdVlsYNMg1Vi8Lv/YzdWURkflcVoTonBJDfyCPTmKjJhZlnFsWUmUWlL7PPFQhLmmNy+w9AWcosa3/7zfpQlSMW7b8h21MAJNaft6wTnXsGLVc2k09sQUwGL/0HNArDJ38x6WGBSQNGiFpMFz+yHyXNGuB+YLjkCH+Pf24Ta8HgPLsfk9FUe1vga9It3QjC7VsJKFKD0ZOClgy/vZQ70hjQFXatmWOtdg45ZsBdoR7dcBkDiUwp7ZnrPS6+tvtDr1DurJbtGTcRAFeNqqDnCedPm0fJWXUvyT5lhEuVbyC3LQqS3w8zmspF2XReG44xohOiO6zDmpewqwMhYojKed5p3oruVlq4vkFnq+2SaoTVlQf7sz/KXRYGIRhSAKaxjLZUPlWewTS+Bq+2KQnop19EfZ4nTlM2tKN85esu3jZWvLswk0n9pcZmORwqkGfiCPvc9XM1UmhHPUCPkPLJJE/IlZ5QA2Mt0DNhexR0qzi2R6dWZcN92/okZoz7uMJLNCZeThlUAP/mPViaPSkiQGNZuf7SqZ9UrXhCcZrSHA3ublIsYJ2pE5F/tnRNurZDbQpzLIJNKZQ6E74p/kLrCLKHUQkOlHcW+W93Fhw8i1pV0hkQxyIXMyzSqOX31yeWmuXZgAXqmEWv9dHwVKPj0eWAgR+LcOZpWCmIc5MfS0lo8xiKMrQifMSvpCx/0u4PfIoGAHAxukd347WoIKkkyhCZHONCLxkvxx01c2SgHJfxMBY7j+3vzInpZT7TmLbENnjvG5i5IiQE2+G//RjR7ymUz+sEZWNQqZFGHFwm0Vt6o8gQB+9eMuvboqCyyxRvOcNX9k1tW/w//zDv/o6n+sYPnIpK8FkfRfZucqzGgtj5JXHcxnJ2TlNqkRc8BgfipNR4RKgThNBdXTnz5NhIQThuvgnJxpCUyiG4H0n0FuwmMLiSx0Kii+F2Ke7mofCVDs+uLLoEkVRP5BVPtNM4xyACRmADK1MkNwZX87tqyVSd9Dt926fh/IOtq8ExIbCBz6MFVcKHV2zF2HkNV25EsxN7FSVIzqdd57J4ijKBm6pKi5++3uo1WzrSi4cbH95WHIjcFME4+NkAgoWwQmL8knbW4ZMaep25H5PsUCQ75b+3oogzPyomuayApJJiNYDhbPO70wTAuTsr0QXCIEWdA2LiVtsRL0NjyOyyZd8OJWLCmrg5ofNcQdXGwUSC8AA0v9Da02Asmp6pj5oQA+92KQSA+HQc1VM6YjiDrBLCk+c1k6O5ZTXkpMQ2R/mt5rSOWY1SKVEmGFx6PAp9I+Qe41I89KZ6DI1mnsrnGSFGmpDQIFCPKtZ9YQbUasemqFmvLg08KUFrtz+KmNujT5xFAwBUYgyWKh4BXg+iMU5m0LqJN8vgg3IQ7wuoFkX10yzwCLNfk3e4fi63lklTfXMqOnEik6BbVECNujRzhloOu9Smjimsd96EQ0WVptnYW8+YD2dxP0o0zwhUHdImFAL5G5CsFX8RFfzi8UUnFaO8ZesWs9xC79udeM+/OMHYxWUweG/bK+0Uh9Tib1z0LTixb00rx8fKdWWaZLjyIM70OKiNGShZFcXxxm85cUSW+RDL1glrJmJOXzhlsQjMcHcQqTwTickk2gFemYMAI28VEH2Ntad4SW2MdiBxxvupYN8UVp8HV71BY19FZOjP8Vh64lC+GHd8DAwTlccI3q1qgG3LDkzZ9yqHCJWGCtt9bpM1/XeexNEhFa/7g+6qTxzNVdaTL+Fla8SSwc7dh7GumaVR1TtXd3BMYCfZD4GzVBMFxgCYRykwOmzL2A3FUuGAHPhU+G2XFbPydrdtRHVdD/RYQDIV4XYn6ydXrqnNFfg9erfz0ARq7QS7r2bgVRyCLUhOsFCiwmzNRcA+j/X4vO28V0I4QPs8iycfz6WzlH++xeOEImLl3TIHfs+9ZIEROgElrSW+64RC48gGRuQVgRtbfsBfOX9bnGqkhtEOXGlguzpduGnO2ETCnwvi8qFXayTZbBPJgSQV/4PiqSGYc4syEj+HC7x2GkQh4uG5uVxVtyx3MHJYex3zg9jhoGRcobmYMH9PfMjfD4YqQgK5ZtFTUmr8X4XzRIC592GAQwchckk9YindWG91+c8Pt/+x10v64O5YvTXkgF8dSR2vDIE3hHdARZNDsj+RvtcBFG1bBwdsPcQmmYgW5wvVIDB1VVlr0trWVcAmhyFYmlfDsFALxDGhKlOo7tKdfcVfJES/WnRC4Hh/4+T+k48wREG6rHri8iP9c9ZrxxWdZ8Cmxv8vb57yN0FtBvsetncr1Z16/sNb2XoOiGF9QDYNTGRcZCCt/+YCxFy2Tca7pUipTxs7tXWaR3WD3+w9HosXpzHxf8/ElbnoWWGpAnudFvL4TFts+Bd6G7UkYetfrZUVJFFP5BbzXa/ET+Kd4ZKWzHiY8VsGbDYyQ43NMDyU/a2hshgNDw1u/M0Sp9v4TtnO8R0jBv7Y6tGLJysUrltLCkU7dTqfDtBiNyOHnRj2TDOkfRedkxkQ7ywuxNjbrbk/hzY0rTAu7GHMqyZBEUM2oALyfQW+WF5521w1qLovqke/zVC3wL1Jc1DequRWXCXAsYxvYj8gxmcWW3ar50mr/AyIEm5QUFUqGJn8f79zoeOAlkMwHgmLSkbDheZmh8tWlPFcIQAYZWcPZxwyi85iLofA+Q/zqk9yChAa1jcD7EcUuUNIz/NSWXlBGQTK07SCCfn4gxOqw3ronYczP8sPQHe7o8e6j6sEB2IJYBzkln1+V4RcVZHsS4JXvojbGCxzYAkx79aZyQsGu2ejl4qYY5pddc8CdwjjZBz5tAt1A7j97endwdZ/E5G7x7zW8MgMPmCRAFrR12yd6eQstlrIjWVPW1608deFPRLXEsQGGHteTcdJ8ch1PP/BADmSNDjzL+vSDI8LkS7bPRcLE8gk0PAiPI2jpunLHKJVtP2QHlfC+WNdP2zOo6mQ8fF58PlyAiPF5jdZrCE8UaYYaPNR/mdphwzmpEacbGzUEU2iMX3O9hTWIgAG3D5sy4G4lOjbuJF8vwG0ns4QKGTGEvJ0S8fFKiWbXDNIO8ArwjLA2IsjKQWav6p/Az9EayrdpyGRZLHGgGr4QUczd2rRV/z5JPk26UNBmzA78iyDS1KiX02AHa8UoJzZ+dyePFqGeobFtA6W3YWNX/N6e/IXyjaisaM0H/gJDB/BlcS78WjTfXzWYaEMp2YmICPdj524usmANenNtJhkznzk9DdbbrN43pbUEx4E1GnKjnCwML4hyc6Va4sKI0u30/pdkobgjzz/ddbnbYq3rM7/zlQVT36c/dyuDC2aOl0wxrvje50bJYXHM4j8zJwVrIB14aTvqNOp39MB5R0YShUy4BuNK6APPKoQN51VDfzYW57PH5IwRnmSsycHBoh4kRS/gImsDsH01QKQti4LPtg69lanz7VDNO9Ck1Gc6slfev0Gj8hSy/f0WRmgx4/AwCDqZq34RU93JVTtd0CsZV34078e+esjqAugggQqk1cx9LM/X27c42OWUIEYqK5ONIosr7hOwqnHa72Bh0ZX9bZzuKy7dE8uO+jAKDAlaFrhzonYfOj9LvK4lMYjxtkKoMrgsuEG8WOhS2QSsuAeVor1GEdbu/zx0yoNRt6ePW0Qlhdo2zrkzt41RX3nDWXLLGoP1TYnbXX18LPbaqpbcAKX0WBf6MzHd1Htzu5fQBuEJCbmEtx3eXV/0AVot4dQWFr1pP1gALX3hiBgAHPXHJ5wVaHMhrx3yfHbMz+KCVrux68flIQ2AgydNGAoSx+LsVfLmml0TkB0/7nHFSYvNp8oHT7Klp9SETjBGH1ywMKtPplL+J8Y8gftAHCzP2k+wTw9ikdcBuYQdTrlqQgnLCq1597c5lvBhX9ah4MBQ9TSgehAGPptFmCibT2uP9pecDPnas2lcDot5RY9rEQF8d4Ou2OCPiWlXSdUBsQNhSQy/O45Et7Tws/zruGRyqCcIOjb48RTfijXhI4D2LcG6wOdCUzObH4XsKqTXx9US58SeSWo8clTL5gCKveRy3wcj9EtMoZIgtX3XtZ4AEmBsnNIKfZOGmlUecE84Kuqx5SzHxrpSI2KACWKGFjQFU331SGYa6pDPgBfBDBTHULjPeQd2SNWEYfSK8bVDTCCLifBif5qiVGx0QOHPFOh0JKYuI79RG1fj9dyKDJOF1PlmA/PijhXEZENILzXvjgHMxLevpcSlY2XBUXy5LpQ8/0uyZTZXDuz2epAyOAYJPvzg9Np4LiGZtU3iPU46eXI+80EkkaUL4+HRG88BYLHlPzGo1bQftNxtYLR+nYfYHV/KjjBwdb46hJfEvBlZV5JM7PDl/Fkx2NT9ZXMQx1Xbs7P6cYypdGqX8qLytas3/QVXGer540A4FCJKia9ensTGIycRciwbtiDThkIE8tldfJZAXvzPClDWFO9croySRQzsxKHOg2RgGZV/thCoS5m1FZoXlMUjhRm1EoYeanzmb1/EB8cKko8K010AL/NNsvGJH1bIteEAqaQ5usMz/oMYv0xZEocF8qvFJn7lowDgpTC3hzq83MQUyf29UixDcjOYqhqQzNndaQxsbQZ/woMXkLF9jt2e7L8hvGMEs5YSTPoSTCCYjlkPqkM1/brWbxKw8mVyiM27Wnn01wdWRshiLpEiAOnVW02zgfQULnixcTcrw/KGPBRTLKLtalKzI5orxUAwfewoqZiUoR+/Bvok3HxhSdlP52YnTYGnx8ZiRrWG1nLuW3doUwgVi94/yXT+SYt9yYYIyS1wg7R+WYBTA2aKqcDPtFECkwwSiWszrwU8rjOmtcCa8rJ/89o8gPIivm1e6huOJjYO3MMzBBbWINdrikN6GH8Cewcgu7OosRi6DLU21P+WxC6ibOKJoHBfiek3xJnmt3TcTkXDhcusY6KctwrC/nD4+svxAVa2j4lmbLCcTM1TjKWXaffc+g3HqUdBhaPMYILVifOcf2sWrmadLXMCMgUkxvwjXyec8Uxlbu69c5e9x4Fy/SUaUnlEl6M3N0tk2NPP4Nn+MmX14q9lIWQhUoe3f42QdXptF7dZV8Bf0bzr37qGeV+GPhzpqK/dvSvaOcItr/Wp2aHN5SLrSTsw3EJvxHIkCkaFHS1xAttFy/+PfVicedEb1z7r1e4grxUlrdpdShC1StoRTgFTVZX5nSOUK+Q6UzutUTT0fCEU+mk5lZW3Yvc8sW947Fnnx9Z2NERcevf7HstvGv3g4xhliM9rtmQpMOTJOblWWjrbyw/ZgprZnlPIYLoIaQwFH7l+G9nLRHpV2z/EVNN1EcM4a0vsGBRVgFGenaFAKrEfiTdGmTq+DNwlMCAbROgJaD2CqCkgT8AjUiMhWCxFmRYYyYXBz/KssD4Fn/W5TWLthY/iLc3X67glzRLlNnEEZiZKBxYJ44yOCwtgmZoB3dQbf679TL94qknFaYrg4L9NFsmYJljAgAOKIzFj/O5LdxM5t1Ps3eah/O0p6Fh+auUzwuQIxhmOlJtopQx5rxFTSZhYi++tYIm+ATN2kuezJnjS/R2OlrOgkwHugwD92UCLko/BsDcuzSfGSyTLpJsamcJHgNYHJR4GNZn1TyoKNIzKjqXBMgm+9D2w36K1r91d8OqWaOMXKe1XDzhwSYTnciaJ34d4ZiIGOru0j8CEvk8NrFzzunIDmt2lN/9hCmcMzgdjtcBc8Yc02XSq/0qTUGOC2uaDxhYI2qFB/mlc5Bx1ImGWX2XLhtotd7A9iK3RcdvWHsX20ZKMO/s8P0f903nXHWtq6my2u89cGDMdBcobMNTYWvOcgULVu2iD6Of1y1w4EbbfM4opjZnGcdo7MdtL8DesS0Rxj0lP1F98YXoGiFoEXBOjH5HhjkhlepgdOLsj4h8+N5io2CtQ97MQZZojqM7DMPqGyU8Nvs4aUXBTk7GY94OiTKKMGlemFrvBNkUGpaKNV7EDAQaO9+YJtuL48+Z838oI4GAMx2UbU+NGzN6TmodnhDOjn/nKjOjv+y10Bi1PaRvza0jLHtqxf4fExrkDnc+OwypdjHt0hHqyDa7Pgu7WyDOIbm+daGreA15WRajKG/bPV8I32A663t45v1Zpz8I2fX8UshmV79TT7LYcnop+Jegap0kS4N9KqD2OL9HeIkIjzByLkJYlVkk/3EoY6KpzhUzMqWOyyzQaDNOHn7F9+5/uT+vpOQhwGZWjAc67naaNUBprtOeGS7r5VmIwYGNzT9fYejpjTfLXhu0zbMt36EWplgXnII6jcA0GEjDNSHZD9e5uJK7vB5WH5jmlD3g3NsmOmoJgUE3viDj2VwXl1SCgTKOOboQnnlY/xKg3uGiqxSdAeRloJK9vClgk6PnoDehNm0USLkrp5U3xpxgOI/YxywDnHOHQXI/I6K+B9rxZCiLBBzeHuENBhZuZmXGG4gi6CV+VWFrMUi3LcDcPKWA7DmpZxtV7PodVKjgZ3Cjmgek3AnlXMrdAShW9lcJI8eBvfWoGRNPfpdjoTFoLCqFdir0jsy6c0mP1hkSEDKmSlSzQS2cAy/fnL4gMKxxGuqZatgYjGVsFgBLlon04Yd6JugaR740SQSHz26jv3k+uC846qluATTBRPA2GSkQAmi3LNXepHM9KbDxI2rZOKSXeUvIxxmuGi51tnevF5cXupjfge5ySZ3Nh0BhUdRa7V0QnQQDMmayTMIT7BysAtsDI5PatRiQeBLjbbhzW5d6+bwiftpSllXOHxd6yhkfkWs/MDFuoI6FEDfzV5eYSbS/fidjOjrTwb5GdoSv7VqarokG31bjeVASLDLDtljhOvfWk8tjKFKX/LNQEqSAPrWa60uOBLQKIYxvu8ceUfDfXt+l0rgNkd7kV6vgqSBWxjMHM6Ru+5rAk86EBka3jh4WODXwRbnBU6VIqBz7XJy03rjrb9FwZT7PqSm7fkudnJW82DqCtz+bpcIBozptjj+eIrj7tRbbAkuaJAXugphd1KmnU8XuyguRxvNY8iWmyl0dCU8/UlUsScO2JF4T26hvgto3MVRH9IXsF3uYIZYp10ZFc1Dt3vrqhzvNTSfd6qMzeHthvuyC9rHIb+c0ySGsWcTMLpAorXZumRsH2s4ShiEaZi7qejO4SrMhPYg35QsKXZKU3JlQxPRKwyGgnmRx2IxLsOJegz0ViUKUWAc84iMJlUyCTTc6msV0WlkInP3GDieLG+t+GZxH67QGeM894D6bshQ0RImFlDn5urDvdxd46otfoLs2M8mWPYsBQQ8SuYu569KZBoCRarD4TgUqNRRMblgg4rXLJk6Fvmp6a3U1AQnB61Co3t38TAhTOL5JAGXF1vo9UP7slzfRYtZoq3OqsAFkeUeButr/oaLW0UsywPN6GJ0KTbhyNyeJPx7roDmZ4bJqCErHMHnG9+N/0kg/dpJEMZmUIrYjX2iFJwf9rVdu5A0zl/IHEHD5xS3+i6ZGBiihfi57Xwo4f7tbeqgBU5+ySU7unqZOpesKqi5Amyi3c2ffN6X4+yPY9E5F6lXaGy0dw4pc4BV8rQlj871fcKDpQ7WJyCsj4WdsFpSGp6+QjFN1dRRmufiIFAge4KnCgkSIQwiyceLlqRDjOX51Hc1PKXhtUMkTLrY/m8WEqNaU5zwnuVu9DEC6IkD3Timh/mTr3tjgoqEbwWf8MfVJwPDdt89xFKKKIKzK/LNe/ZI9435cK/lxpe0gRpUJpM/EUtFKMNv4/okCHyHHumI08QIK1K1WhV+p2y4E/8WZIyCIM8Tn9QSj9fo72vztS/w1h8JnG5X+ZgxPjbXG6N67PzlNnAU5S3SYKDXsiX6uCbmznSGloxJSFYent9meE1QTJ4Y3AIMh/VkuzSeXOjPnkbAZVLhGbk4GzHstA1cT8PGd0dsnUsNF9oKqOWTsFZN0s01YspwDYLr2zO9SrN0RqLMkTwUd1GcCWlXnoWJtvYsh5CM9gM1Stkd0zfeihQAoZJpJiOUS6R03evF18ymWXaqylZmZCzAEpJi2QHRF/JUq4sz1gU9281Os5mQjDYcInZcOTexbWx0opovaMYOx2F0lwAlgt2J7UGdu29bd7g/J3hjoFbPBOqW70rmBcKDaZGdewhjhGH1fYrGVut3xHh/vy/yZuzkY4FFMwYoVXNhtiagE2Ah/oyyJB/W75JJ691f5ADpe5qYI8VfThARZKWKDqqyPMgfJ59S9dSpl3P37mBcLwXexPDJR+XU9db9mCeW0u2TZ0hnCHcvC+OO+jyjhX5hIJJF+JU+nXFZ/XYoudfCV1FJQD7UikHohNTL+tVEH//Km8Z00/uv1PiBD3F58nb0HN9sVVB5NtDdW2OxIbiiHyMSi+h9yZ8+CJAPJrdINVHTZz6SeLOH9MCNiZO9TDZR7MlglX4ilmv3/5TziNCDDENueU7OneGGQKd6JfpJkZYuTrlL2KQIeZ5UYt9JXlnyp+iFARBdfuirgamPkqemmNLXxGKxfXUCTDZqGycReV3vzafdys6HLXVbcQ9ZBmrv55uibNFba05JallXkC2QkWYRb65r0xn2Lf11IRp6qgxKObz4jQvBMitoZIClhzPe1ZNu8zn13N6lGAc2YwJeQJvYe96yJUwK+IDj3YzLYwxori+hc0vMs38cCR5+kau7ZXGOzAVVC1r67H/QKQdjOZriS070ZGtlSgbv6UNInV2VC7O6Dfl1o4NuzFNDHUhL1p26oNwGTDV95E5Ont4pZkGLgnLAsvsNUEd7EtE1fvxyBXyVfJUi0pQB3fvS6SaxZsFPg9Dl/z/PdXW0WoZo/Hqme9MxiamZVR7Z3Z76lPwXX6AuS6pJXwH/iaqw1LUPCSQ3RHkpW74rskGfqO7/Kv+KyOtD5ID+h+jLbW0g9IYI1+pdeypi+6EAO+YP9lkQ8DjTvznxV9nGFarYZgxum1KNvrhiUzzkFx8I3FFVhII8zjqUqNrav/HRDnUiz5ZM2nNQ/tozyrxk6LukL0r6nHIjCpuavyhMdnYVJJ3wC6ul13Vli6B20dhjGKY82dHhZLufTbvgvMxcBXl7xZBmzB/Umk/SkTSj7BWnmFRdCl/LqO2AKq1gzQHPrxNgpX5gKfGENc01T0LFru1VvD+7D3pAs6/Kjgkyw6lD3jJQib01v9w1x7EMBvQDVf2qYNzh8DwuDT+xRkPJJ0+I24Zjxtooe69gxS2mRUVVvNAUS054Sy+8TNZJYG+hUqLqsu7l0WgVI++HpD0E/WL572iQNkGPxw2dyxo4bT28SSUoybmcxeTIp+Y8yyPwaCg8ilRLGyeKXwxKcVJfRknXVzUOIp4iCFXJK2rIYtzbyGoSrkI2DQ1eVtPu+iYkBYQahUvA8ekae5rSYSzKAj/REM0tS/5xi9xaxztQu8vkiuGnLAxm5Vw7N0LbfeGa6JnpbOV1MkplLWSnNf6o3AP4QScBDdILlGgZ2wvZIOT7407NsbSmh/UKaPmPaW5nw7UF030HkhDWbxUmAjuJylXBeGRRPzehN3cRd00fr6qQ+ZQRKiJl3Q7/ut7zeQZT0frqn/XWjgbo4oJR1x53t4UXv68kEFFyiLdoIad2iH60grM7bNqt/mftLC6mgzbCybPbVXSKOVIp2fkOWhZPetB8v4kakkHsHzbtnWdh3NncYJo1OckxYl6HlZc0Y3tyoR6Mjj+K51fjSvXFuWCuUJWl/9uAn4G0mm35b2MOclYmdOLKtxLrKc6RsIfHfF35l4jauYpxvgUfcVatYsgHoQvHn+Jb/vXMjWWmiRel+B94/n2KUOdrWSccQpGFvfeVr8D14WGmjyXPcn9jGrrZLs4tPG9JEy5k9EnfVfVnTy15qg5Tdls5xGrfkMOOW1XLQxDoxcpsiFuQT0YDaqDGfu4crcWA7dmhkUbPfcPuxkU7018/krWW7UfW3bnIvf4BMDMDvft2y1t2k71ktQxzkND16vrUUj+9Ctb2bwQELd6GGt9gZlpI8zZnN4DWwmxCoqj/hGUS02jWLIMp9bG7MPUXKYk5oPJsqi1DMnu7062rR9T1xCK+HPKlQ3SpnBhPec6RjjxAheHtVQkfAzoR1+8g3wPQsuDCRwKybdGMEVdgr1tJWt9TaYDfXOkVoxve99T5N/4KaCCorlU4v2YJG2S3COeKby2VoZiXGIkQ0Q3l+7gAMiomaJvt4Nhgsb1n4JjZ2cM6zXJrWpomQj0XRgtz+xq14BSjC/n4ofNUYIb3mycUJLr7IZFAO4SD6Z5X5mVLxwGmScOhjAaIwf/1ZThe39ea8DLHNm1+z8NtyWaH71yR83thosDBZtw/Dr3y6GXEdZuPTEgvYFt88/TdNrsmaHuwBWi58GaOC7MpZmRuSF/8zRZj5ycAW1evjvIFuyuFr14cSGbTVqrjKPD8KGViY2qseb6I/0oTKXRiTaXKuZkuzMN4nlPxnJHPbVnb623C51fRGp8XEOsCm7MzphaBIp4JB6qE2fGjCf6JCFOpLBmgUz6JeSmYxQvjX+JO3gN+/dWii5n6EfIqF9V2BQDTodb5lEMAfIV06Wu/rm4DAIAHVWa3YCzFUbKyQ8XJR2L1MLzqbI0sIvhtcNaqoegRsKziSVzwJhEbtJJai/ck4iqlJOwtCEfL6Ira58L6ODQstF1kigdJcZKXJak1HkGBSMHKgNnVclhnat0SXzT5HXEsgdkRm0KXPcXcf59SqlUTredJ5SlDICKbpHozoeOneM+ZPaMzGYSWO5t2qQzAkZC2yYgj1x7Y/OaABUolzGXwu9xnhyL5T3z5wd7S2h57m9j7Bo0UTMgu2Yy0LO0WdRrVPY5Ynl2aUOR2GqRrdcKWfQwmeIKlkgEAIrKfZ33KRmBqXJTjYbpJYK0ADkl4XE0jxmvyF4NIjLt+tjnmQzftMAg7hRbsu0vGQvE/hkbPv9PhTeKoXKKTK7KRQiE7yBBCailyGNydSM6v4UB1a/7tx2BhqG/1QbGryTK00Zfn0YsLX9A+3ssEp3HBMKMCrfnxAwMp0KAK2rYeXxVD+z83DkJrSJHxwmLFsXcAjD6bZJKHJnoNZJ8t75xGiRndXmqodHlUFWr9GnsKlNp9lsVTMwFUN76K3hd8Ly2lbVxW5mlMuNQhiWv0ZagPuDHOiHbL2EVLLoqpMIeHlfbILEaeu6UB3W+ECpPpsuGuz52/aPFm9VPkx7BgbC3KMLhhVeYo1ZNqjhwArAOEHcH71o/zUwBirjX0GOSHKaHnRtQGxhe9zN5YL1cGueIJuwtapCZY5h5rx23kWAkI0lfl/sHMJhWi5RmeEdDw4xGMdmMuSuDsGMT8lR6RUceyd4QNVUDtmLkytTrnwBfuGtoVFma3iDKEY2mQBzG1e5mBuR7aPGj8CsagwR56YJ8vk5Di45E3HBfn3poEyuaqZh8fMYVe0O4l8CTmNgM4AKWU2U6gzHlFr/r1qoz3g/cTKmFoo9kwi2Uj992+mxk2rFMIfL7kxYttb5l+1Nw1ekthLAblM9zkFlAGxYnXxb70lfwrvh+HFzSTdh4kenTO7kEJDa+NjaIJ/Pzw+xRGhBt6pQNB4gKkfbscyni6aHSpQbtwvHLmVpv6GfaRnGiQKwI6ynqQodx8KLD6swJ0tx194rfw1xHcGafEghM+X2iCwB+N1Z39dWJeh76WMBHP9WFrvhr5FVfBWfxxIEhUiKt2DBZnlISM87sJRoqJItOsHUmZGTLAV2nklCXZHmRhtYICQ3Yp2DjM8ktMQ0sv29MCvWpFXN0wX+bX75jYco69BHFOQSivnsChFmyMJS6qnrI3kDzdS4ou1MxrTQOIGbsA+/HFv2UWk9MwvDifa1InI8HVQYQsT6wF/LWDfSXcYJxWdX9li+dfibm7Vz+4xVCqwReLu1Q1PIXb64Pv83eqfNYNN/X2AGWm5MWsQktfLxS4uG2u6ccthNfjzcAKw6VpHo3NA3yrtvq+HwcCoCJYcX+VI0Fq9FFrbOcuHNVPU6KizYqp6HXi5kD1TRUVJX500mqCVanI0JyHJomHqVIzXkodSHSdrIC2lblG5f0SauDOKEqE4/omKXIs3G3GZ9028sQdbuunbIzG0OKBrYTQrfJNj2yzj5sZ6AzL6HBQii2+hXh2HzciINxbAqu1f8TtHmsvTRajN+oC5D8PMrYmVgKTgS4L0JyD7D1IUyxcHV2rw2cYeYXt/39uW46SVd/V6K08U6cEG4qmECDUFoAlHKeMoYFJblB1fXB9sT3fOWCzbADP50YlqLpZmAm+yWaWUUiqprnnPBjRZMCLAaqkGzqJ2LBCnCZEkxOWYLiArvgOTBTVe26ZV9nQw2yecgkUytLukHOOTc7WcLD+HFf0TcvPRZeYuQ6fXaUJ8SmrzTNFu8HhorwrMDlN7jOaEkHkY3gtzESa15+uBRVvj+Qn+camnFZdJ9ldYAdVN8ltqtBFg4LUDnUDggRMvWnAdM6xm7ELqwZfc4ODXEACUZdIXok7SnDJjhDE7k1VS02/TIhqZ5w8xkvg3b+X8DSGPSu94UgygQvMATxrMCg/xpym79oZ7c+Y04EedS0FcsRPKKJdIpIMDUgXLgI/hyUoQvj57b1UoI/NtkwZf2Wod5mJieuWM+Qmjje0ZZ+A5QH8AhaO804KiTc7AgT6gS6bf7U7Gea+b4a0uuQk8FIybduHV9bEHE/AkxvT2hiEPwkfxcpzP45US6DGdLDzV1ZnU+7nexcK5bcN4JjJfCzDZ3G6VDwy0hATCelxCAGtzNGD5juRxiqWFXWTdqQsrIm5Yd2FfZYZXH0hTEp201EapeQ1BAEjOc1y1tLxNIGZ9fVuVQ7UMJC78Nd6dw6AiK8/Y+Jaw9/zZJgQrn/gXegRra2EsNNYDvuSBlg7w6lWpBCeJ4zxOGDIQnDwZwGsqZJSIahtBoHuigjZi7ZRIsSvq7fcpykduQlyi2CvA5h2tdoq5nKadrLLyrmn91pKXt9ZWWA5SPbaN1zh2AlEc/j6+qJ7dopSdQrUaa06W7g6P5UtDEGYpZgIC+rmCYQ477MS101dsxNC9q+WimJedKO7pFXmp6igvFRKQTbH6BXxmNnQH4PTPZ6AQbz7gtMJ8NoIOXml1g1OOD3km2EeJwhxDCS1wTbYBG3G5xYpg7IEjnnr0clhKBNnpUtntSBStyWoleemHli5xpvkGcXrFcPYi1JLB0BbLskx2m7fm+eUESvgP+Cg7AHxxrG2h9QiCa46upj/O87zNgCDDIy+1WD4S58c1qOe/RsC6FWZoMOv7C9/QK2ly1VdJKalIb+uswe2gMROYTEgjWnC4C89aiUsN5MJi7IXUo9jnS4P9YvHIlXv8FDP2JTxNEr6P5FI00OoyD335M8DwvKy9JngcoNOogPqPTJGFWTgJE3wrjmBh2M7StAbFdKShHCDxqm0eiF81Zd5/nb5qVnmVKMDs1evLgTttWfx0cF3m75/dpPJlPt2WsJPfrLzgy7u8lVjvzDzk63KXYzFCMQII0hlvduW2VmDpjLAJx0SXdQfn+tWQDvHDFX475gqpZ4KDfvq2MMD9TZBOPF+fgX4WJ97XfeVFbwUV6k4zDj0777h1/1ooYhGkgiVYL439zIzjY/mfrHHVNVBf5K+8itXuktasYXn0MjguEckhWfxWQcSn55B6KuUoJ3GwVNFokVehfyGOjoZ0E3r3KFmuQ/4Qcl8STYHamJatCWEhiLdN1pszOnt6wMyPl533QCjrMZBvDd5Ms/JYh2ImwJJrHru1M8g5nOxMEx4R8kb72LgKjcUYCPafE+55Bi16NjvoiWXcz50Udlo0/1qtU+7L5oDLOebRrm1/gllo+Mu0YcnyVyKCHIT180CBSg5C55d+sj1f2EQ1mC6Kn8V3v64Hm3wggsbpkbVdF+CKW2PVcpW2b33iQIcS07WpfawlSxp9nPyQC3fS/cCc0dj69ucvylWWYXMy/g9W/uJeapZsL0AkTXFvcSipd23PCQ3xFyWGOBpbF5GiqDAus7GVQ9w/2sAKy4po7c4mQcQUIvgMlWVnmc5H94CPtf93nmuW345+9WisFLSM3OrLdlSZi8WN4YrU+AIHy4CIkRhAV+RanwNyz+q9xNOfvYJbqkJtPZo0HQ+3t9ZfGOTIIbb6dOmBfXXz2HFSA0i/f9G3PD8yMkt43fEARdTt+81XrKgBob4WLpgvI/03EYsbajx3l/iT4KfRS9gaD9WmnelIuMS3h6iYLvetY4CNkQ4F9QhIYVm7QKRwO6qO8S2vc0fBitDpYmy+TCiWLBxK4tq+oEjh7lU2CCZGHcZucSqrOu3NJOFGm/SHNQ1QvZkS19sUa792+jLRSz+z7hxJluhzePSBXTcnFEU1EPyY/I5ZAd1WWsEeB6TXFubEcbRPx9WZ6gd1NIFe8muCgm863ieIeOrlWDwe38L+I0u1xQzGdzwkujbU84n++TsLvhU96hKZImDdNxHD+HtMxcrRlOF3o4RvtR4FzHBuY3GL0zJn7opb07NBxClHclQrlsKC0uhaDeVgTv6ygRuEpRXN+ocZX3ss7RFtzZh8QFXiBBXg/vylTMLQyaQod9cGugI2j2Ao2osoQX1aEPJawRJqWkNVif4VchTU67otK4XBlNcOWK2o/yVNjWkRtynmdD/YxL5YtPQvjdcy263YAguzqT5RUhgel+7ydxaOfHeWoTOURwdZUqnktTYKf88HPnovmQ13Zkh1MSqzvi7L3+xcL9rcuiRmuhDTTXla+DdSPfoWB8lsxtOTxK0jh9209kZyEMqpbP3Mprljlr1jKBOBbgF4KzX/PCEWU/e0vVYhHTgVq/3uHpeKvS1NbOaJGku1rb5SOIw0w+XrNyHBF7etZZlkp4giaT1Cy1MFDANcZ4vLPULuq3qNZs9Q4TnSoS5W9PsIxEAqIouhjCk/XRBi0Jy7YUx9JSA+m+TeKLKLbOhFUt19tNx5nSkFy62cMnJBaBYqvV5jy+c7vvsQp/xhxrAJ+WVqcQzI7bM4eM6FVI7Imc28FI9YtAZJfoCGu8PWLdhVPfGj/Si8RhWn+JytRhtl+hpB9RH+xyMkgK1d2D7its8g0c9FOCAjrHtapXem4A0FsWOzHkvdTKx1uYs8jYSM1RI0dQ/ZPSD4cNgQ77iPJrEqkM3920ZzrtOttoPKVix6opbl3nWnoyHsBtGq4KSNVpSbPIcBwPqaAlBK8TgYyYstF1Fi7lbVb9zvehYqRh/DFWj48mHuf4eSQJanDVgp2rpPFG1kU+oas9D101BVbweDUts3+MM7479W8Y1OENb4DJmSH0GM/g7la2UbwlHZDVwKWe1hN9xa07aHbzR4TshcQX1vvuGC8KjDBHN5SL0aKbEgUzsayPdMUXVlNTw0e1zjC6UGm3eqm+xrPANeLVACjQnDScwXRRfq/2ErUXxf44JlhZt0DRFCkFDfsmkaoH3z3uGX98UX9XRxlGyFuoD+diC/AKTCo8Ej0GGYdLpUi2vmbTStBgT6GIAm+CxP9MmPhRkGA+f/UsbB26qwBxnha17ZcwIniN/ovZSZ9W/XQ4Qg7sVYjOY6JW8Q9tLcQbe+aJoRqpq9wZd/NyIXASg78G2B8jqObqVJ2andg2KjmLdJWyZAlK7oJMxH15V3y7l/v3IZMFOpLRWn3tFDQvgU+Bl5nPt+kS3u38VQCh+n4xj6gknBu9DUj6wAkfSJq9nQ6KOyeCn7yqqeDCLnGGokpxeCnKdT5wJCAiJo5L+PpKpOYBogsQa7i5wiq4pTMeVQO+rp3wK0Y/7MeyGTd8/TGjtw0bUAxnVMzHTYzF9Z1L8MXuHFwd0hgzrTqwHEyTT/EShxf96LdzknxE1ct8DEzwxM1IitwjOJfzO/DiBg3onJP3lkOVJ5WBCRQj2zbbinOxFJzzz9kIa8DW0RHT0Uniz+XgPO7zJVB/iewtnccxe9hhYE3cEpQ86oRdrDQXS0rbGTCKcxHRRNq81n0xeWRIemmAGxwCHZFX0i4u1RnH2yQQrHkN5umUszGj3bENBeO/FG5w4ETgMmuD+tk9bcKO8IhMsFIFJdtzqSt7w4XMKHmwysmPFaF2nDKIyWqipOuOucR9TWEsKqqBSBm8IpNOPDOLjizeQwxJ9piOWXwZpiOUBfhEW8cNym463z9vWf45YR1wBtNvMLhJd59gLis1zzZ8tU86r7LdtmoSAkDvNJRuqZ2+ZPGyRFEhCQt1QzY0CSJMmsp2P91vpuRKFBtyGGkMxpxz9N7PNXDgUp/jHsAcFB0xy+lpN478UCcdqrF7v84bLdNpsHkssTKwbTINH5NOzQpcIDpiZo3IVy7Ji9A6+shwnX4UtUi5JdKVVc1vqZROq64BpmdXwRDIC7wO5lqqMj//U2cR7TxciJwlAruu91s3KvVdGtiPeCHiwtNQ2+FX8Nt5eXewVstcZDT7BR7CwSJT3Mb96Vz9lvC6k17gwbsogzXnoc23Rt98erjy4rqIgOE42J0hevybZFdvhxvk8Bw/tuY6GH8kbYLmt20psKeVztW6Kfgcd8L0Efx/mmeQySEPWAyoPYdQ1O5/TH9RztbZFEqNhkwxs25irE0eflMqw3fO5wh7venyVSMm2j3pUOi1YQcG/O59Na4e4tzrPEKqWM4EX7A+M5v8fRL+yzraRRogkJAb+qVScI4b5ikzE+g1YZEQk5uOz0tiknaqhas0T8YV7RNElw9VIfIu94uLX+ZthISndku8st/ceeotEjp53w5+U40tUlx2CTG2rmKfqZ0BdAvVcDnNGIO9naKCPTXjAvV9n7/iluYy8C2K2Mu1GKIldUWaDe6H3n9ve8Jximqc10F4layAOKvfPpQlwaJgKb+DdXM6D7XoiebWDD8jDxsF5IvRNHQrH9F6Nt6PQkilDcc+dbg39Y6I2igUFULfhrQ3tbBfxmhSXKI+oKkAygdFcjYF0xBDSAuoSMjJ8ulrxLqW8B6gGieQDYjGUILDYjh9LfrE2NJHYljcDXybuABrL770o7uX4GjOU+OrID5YlTHH5TQXYkrL+qzUuaSfhhuY6cghydn/oPxlp7Fu+fS6SknzAVv3Mzgjp1I7jw/E+Mk4eYz8uMPinzJtKoJOFbXfIqbWloJTS5J6ZEs7sWXG9u+68Go/2/cdtyrf56x3UoWUsZAf51+a29wqnuqS9f+1LH/ey8lEu6Fi3jH3UTLoJw2uJj0zsCjiSzzMMrD6SnRscjJjo9Py5XYXo4pEwN13YdrbkysMKEadMQIOLACPmuuqtytHREyR4FV8vGVWn2l22rGSfhGc2huyg3Tb8bD98DVuBN5tAy3Z5mWG1IAgHnG+N3/AHMZVQENt8GpMum1N+MJ7L116I8kJD1oWBxl9UawrhthZspJPXkLqdv/9TE5zHk42HG1L9qtNbzbbkDWJVVy+Qcf5aR2hsLYmpfTAu6PZj4IGhcARL8o3mjtOWJRasOOLbBAIT8brNGw0ChTKq/vM7xMiKWMZ2/LbyLeboI8jcpY75R2ZBJ9JwsKI0y+kWGXVXO1IUasxrnVAMTdPKmOM2C5qPp46mwqYVXgwBox947/3sYPinXZCFNyOi6LIkuR8A1EtLBj72pb7RXMXVMMvkDU2AENLeG/1tYcmi7tyQth+uFOP0CHDrG7g2vMwsI1VP5Ql2jTXbbE3yfdPWV2cFy8JugjzKj3aLrpNeNEBU1N6ntpZHsruhZnszt5KlTCCf7dqETVT1KJwIpfg6FzzoBkym7WTWAGv4C3cFBDLJylYwfadVqegvsLacNhFheVujFfSXyQFJbAI1NuGWfv7xG+q7MYxJ2p0zyjsOaULJCm4ITmuUHFcWNHedgll88dVFWyGjKusHqHWFYiUZF85AslsYyXsURu0ukS+GxBrtFOK+s0ch3kkk2KvQaNeLnMiCmcyewtEQGYdjOE/pC1a+KX9LiZL839Q2PVuCiyYIljokHl4wk3HGdxTsYA+3U05ZianWMvawPEWRtIb66TgbUl539BVUdX2YGTobTS1CV09FQB2zyW8DsbqgRl+GXzLucIFdx+hH0oFw50hZVQC9H6sAGkt/UYM2rkmDYKCpePo5tahXjHpwePh9ZTcnA2WfgXVFf0YvJbFKCYWL2wEP940slCzuJf+ikc6KKw0Ug41hvj2SSQ6tAejs0ZYplawPGv0oAnjzYJuu/IjlrAP3jaAdTE8oExsvasqiAwqpmuOUddQ5V5NikcA10hCY9sP1/hfFsSwyIca4z9x60+eq0wwMSocHUNHu7VlcBu+aIyiw93M7LSvbva3e9oRbuJXfy9acZ77BJ1ik4BqcMdaY/YFa/eSnyaOamXA/iCreNPK13hOZ7/oMUr5pSiIyszJ9Xh7FDOM4GoFdfRQms5/WAJBEIPojD5LQwkC9qkXOY1zOKdYU3qY76a9kg47kapekd6k2okLkDpiBrIqGUCggvcVcuOZ8tlESe8A6ThYhtb0JNI0O7ZQXexkbmvmzphqGXxM6zEclOVrLcyhtWyQ/azvo2XtEfT9ndkwY2GKQ4vvw3gDpshakJsfTocnCx5G3WTpLm65Jbe6Rc7WC7TJ8X1bb5h+2orLrn32p6IpdorxtqapOZhGot08DBXsvVHfBRAnsw7/hNy8vLTYzfHao63vEi0kMur1jJ+/JB2IKLVMl4AgQro12DKmgPFQSjJd2D40T0cm5IIs6OjvTUbDOG2u4GnHZl5ZBKb2+WJEpkMomhjIfUiZvCgodBlXzru2BLAHfGjCEc0J1gOa/y+XTCvodb2laDcA5DxdtNMN2Oqeb2wss0IJHiDINSc6fcp1V+p4ouhCEVdelEfdICx0/Exml+gNgTecys2JKPDZxsNua7QroGJQ3w+K0k4XSneHhRp+DuCa4e+4WyRa//7L1W9rHse6efE/lXkG/WhBFiq3qKWRwrlGC7RdLBaNcCYRDlAZste1ED3XxQONYRhLVRwRceAsQEFAN43SAjyo/6yt2+p6CNgH4pHFWMlm/F+7tp7PTewRDSWOmNAVdIYanma4dg+ec7Sljq2upSUvbx+da2qdi17qzSmMlrLmwIERM7EqoN5ykcn17/1YGFpjPyIxgvRX5fOaXn6gWVpSI8OhSAGV1Clp5jStF/fv11Bl5qUTL3dCCMDpTQe9Cw/Ibh6oiRHjvhAYw8Jcm+1c4RSwwjYd/7neIPBNzpjKqa9rUjWUc0RBc+JoQN7J5KNXHQNddMQNUOpA1By19D4l92d1UqmOZvywI+64r0/4xNS2OV45UldPHFj6K8SctnCug0tMnCh86Djf5fjMe48GIJ0GeTCBd0bUC4ktZs6AD1aFcvcn+X0Z7JQAg2gUQzRVQ24CMq5oBTRwEF3KsKRM+UQWCqy3+9HKJyUNzSTtXXed5YW5HCnzwIsUZSMuKW/aKvX+Be/CKmd+1LYequ7rJYiLS84avlnKA0qWYwJ862uE8/z42ynxx6alosnkbtVflii59lIKcNejdKEdkuRbPNyeQxCzvSbd2jkhjLKOPk88ug356oP0PdCJrUFMHeK1CvNl5WWZKNC1HT0gxaf67HDbtb5fCNbQp+35C3nZA0OBsxBl45wCgnI99Mn5U5fnENsQqnrnLqosfzfQSE0iL23xPdZ25r3y9PUKM/yB9fR/DRyXvQe3EEm8hNOy8uXtG1CeBYzqBjQ90flusyc6aUu4dFku3JabFRnZbnds7EEobjQthrfzpBedcpUS1yjaqJli2v30W7Eej/nSJPb2Bm3SDVW2iTyOqntOXFsMXIvMr3nwFmVy7ZCXAXpx8NW8nG5WDSwmn/jzzhr5KoCfsQ0JnrkqJHD/ZKXMH4RpzfP/GUpXC3ba5V9uEbey0yoBM6diZ1euGVjx/z+Y+ZAuHvA/wKhFzllnlPI7RMPXyg76pChKXx1uarj2f4rJT0E68v+EzNWQmsEq8j2Ynls6syVLAsiExqbn935+1ufVmiX4X7LVosCoBCIILSNu5SSI3FFA84nRYMK7+WhYllgOKVdxEL4k0QK1sFxETCAa2HVtgSgNu3tNHeezNGktSv+4RRyOeFWclX8ikYs2+7obzr+ISNMtG9Wquk3eXLqWkyW7nO0xsZnac7Sy+3mOklpcI8Q9bX/5jInOCIgq6yazIspApzlL11WcQdS96zS/OtFrCIKSCTodeeF6Kc1Gp10p6XLFHyt8ulNfz11khsQTOYJq06/XaMdFD0yBdR1MgEwhSGUJ2mvByZolYUAewCuySGIDvn5gZB4/i/8Z5IGquiBQUjR/EFcwk1QvVGPwMgm8vCZuINgSwW5NcKvSslcLWQK9kA4nQ9db+bdzbT94h6gmY1G+H1Cm5b36UatvMV3VAj+ajpfbDrfD4Rj81JxO4/1zDattEbOLCdGksBsfxAovt/S6exoip02f6gPUrFGKIMlm64z2uy7eKzXIbCU86thfAM5xaJZMy/mqz+8yeqmpTmPP9RavBQRpi7pIFt996rlLj4h64txV59jX5RkDf6h7WvR7ToQcYhg7FP1OjM7CJKqeAq1cDYA5tBvhwnlVD6SBa6pIpNaz+5fOT6ba5+VIrEcvmFCvooaz29BxKGICHvBxyjdpDPdR4nc31DsaJQK+pIMoh8QVbzub4m9RPEiWMiPtWbrQPAkDLfpRtCbHXkTES14dnHtt2b7ZHdNAjlVEE+PEJxPhMXOCvaac4iJvHrnFq1pNhJkM9zduvA/5Wswc7KbZOQHJNr4R0jlLYU8feAOAxJz+DDqvJlZPXkk4kGmFNJW9Sl/IGeske33bzyLoeQoMj5pk25qa9czkJbzttHp0OQKKvxwb5A211x1Hq9jV92omebO2WRXaQmlypYJ4lq62l4sPH0FlaYxaEmImhh1pNRNezH3932jn6SbfKFm6TfJrLF6caslXTTgQiNgM6RLwblW8+RaQg7OKUzOmiSeqoiolsAOTBIz3EEBPUyUyHlUr7DGwiIj1SpJ5JqwX0iz/wGuMWW2l0Kn0cIhYtYAP90bwrjLeBFERC6AtugLJg1/uepoyI1+asySzsn4zvZj6/8bQG+Y+T1ll5unmVU7v74cS4ZwOnR0woVK1PXWmGiFqvZHRXn9PZ1HUFFI9ntrdk4NdWQcU0/cXoUK4OErbINCoZLwFzpV/t9nv8q0Kfp6lgKEqPy3AZaWOKlAgHXvA8Mzr7tpnOcQtqqqayaV0Wu2dhXSqig1+iuQZm4FjSlWXylFJpHRuWNJvi5TrzwdemQ6dQrMy7fpE0ckWBlrbaCZYOFoLqcikGHbPIqobiAx3vNFNsGv+arbLEfdupRNNG8EMRYIx3iNNYAozTt3Eol54YxBkOq7GTRCPMVH7pyLFDkwjCgXDC2j7Jt4MIvSIpi/rKBpmvFMuL2DPch/MNjPXjeUtYcW19e2XFuyQmCcL7lX0OBNhJAlSvOMoOCoVduV56sBXMcct8S28GdU5cmyKce9+bfciS4L5nYWc1KXop5OmPOG+bluJcXXhjnZbQ1MR0rjL8ZmySPKJ5lEELCn2a7I/eqPG5zvqZaseYRh01sp7luxwga5u8la3CfNebxa49rPOGZGwhPLtAH6r1tYw7DiPYLlLFvueOw/hFz9B9q2UWUTrMzKdgQa9W5M1xZYlsBFHQnL15rJrAcE7dRhvPUlKZWJLa9Fm0vywNZUmvtHDAQkRsHiSMBqJ8+bF2ZHJF5h3ILoj7f0yQ3KcuJhTcElpC6UfuTaBsbQMYMnGvp0e0l4qYBrLSw/VlfXRye5GJgrSqXN9EKEpMliPSUJHx5bh719i3sLmpwcVRMJjJz42xX/pD6swt3ILF6IumDBhkbr68dnCpw6yT8KL2CUETa3oySPV8Jk1tEqqKH6FJcebfAPUklfZ4Tu4AsIOKggkwpqEE3Zzv9sg74253Tj0/0DOTEtvnAottyPmUxupfLzfO80WyEQXFmHwGtvSSWywsivJDJ6hh7Kvox24Fhi3aHXMNTWyMrpgylXUJyfU68vvYi62ObKs9D3XTyDU4Ul7RFzpziLN30ke//boMI2jZGWJG1q6aAID5Nc3HLhHZ6Plv0zqyRtkxK6pbU6xfoYN/ihYT7S641+jHOKvV7v8suC+wi4jUyB2VdjdnPy+vgm1wiJH4La/N9skmp830ukizYB4sIU9BoLV5OlSXxCVVVFcowcFDnqQUUEFDZ9BSk4Ox6Sh1KDO1J8Myc4sd890qrREM/0e5UZAbW5coeb+xYS15Q0rogqOy/Ozv56bW/fK76flefMxlHj/2cXOq1RZrLUfvPvDSm0JP7Acbqs8Q2d1PQitqwEb+kaTYUQoadOhcSWlIyb15IjSfBEEBJiEIp5hF98ew1cu/FlOrCpxePDEnp3DlCZS+DNLpFmIMfO+OyDzwj5fioiI23Pe9m/94e5ocs5xwumEAV3BfA4Y79NopVpAiq7/jmdmLF7OjukVUHo0UoMgPMJBJiOJFGSSjvcZ7u/w+7Q8VZoq0iccFEHYSkcpThY+Oxm1Q0xy6oUN5CCdouAjBw5F3nZ3xgDpDz/gh/RxnQG1rGuCTxHqor2Y6OwUgqTgsuDyKZ55800YJg0CODn1C2vmiMPUT4DiuI0w7bP0NnM7ebcjr0q2hA01HJsNZIxZ91oKxXFITr68DtopgizsPS2iNjlqrCEkGPkzjN9ivGKSCMWgOWNJ/VH6QXM7YjLhaOX65DEGS4mHv3alOSF2HD3ooc2XYyRptPe5lOjeYBffmHAKAf4go9J0cHDfWxkGkomtTm7/YA3F4z15vRFmKQsuA2VM34B0G7e5VkrVuYCOoAiJlJlPNiZzzDjnuh8k4qhi8pUmEFlRPODyJXx9knq+hlRUF/819g8gFBqTGc8tnj2M33IwLzoacE+94r+0V+5LVGKEpReEvuJxHO9hAPVG4wXqev6/wa4U48Z+ByEoUkJ+JSZ9fFserUPikieEmSkWXk6lnTPNIoSjS8Kb4RV8fIU7EqSIH3TQyCAY2inGmzoZ3vhIrhKQefJS1pHGefgL4M2Tld/c4pnfzaKb1SX3dn2xgmdIBhROInHgQiD6XUzKRmF+PX5w+luwnD7GJmKXmmk7HFoxILHM1VvQLZmBb1aw/bmdwae8Ttu1u6mzsDcK3ejejTiDHiyYBYWkIoz1D5fmlaLwjVKuVVHvJo5gOl6orFm/hLugE5xoW1iBPlpddt8BtOKWYkT9ci5dNwZTMFhKVuWqEXWbj3ILGWAqrMP0qX6PCJqBa9qhPHFYQAhPgsiybJ1eKDuV9l02vIOioiallUfqF6Cp/TvY2Uj6aRQugfoVzfbTHMIpHrt+i5sSP8O5QMvnSuz7xforgLYIwNbXUxX7yqT7R9uxaKXXsNpXo4j1DfCexW4escuMe8prpSXMi1P7eOrYkWJ3AVzA1ELVZjpBedYCJW+g4e3kOuiWOo/89DC1TnJEiHxpjvepH4j1yg/cXhCzBXZHQQNZ7EUa2xmIEV4hXQd2Jypoav9AjmHbe/Qu0uj7CZA0ZLuga+DTAf/1Db+aJaKZlE1uGba96CrNST7AdxLgzYM8qL4MXmVTx9HugB+MXgJ195ZwwnMNavouhIQ1FtxlxUFueyG7AqAfQBohPw1dy0rTv9NqPUiu647WLjs8h91W0xj920Fp4kh1i5voKZnR9RLRBgn4zfNOSGZ2Oye+J6qm6G9oPKiTZN9848S1hrx8m8gB7dceRo1Ynf4Vgs7sEACXUsd+kcYGXdE+CTkug706gImAU98Fs5OdZYJ9J6y3TEe0QcSlUNcTcbjzYx5Kg1c8M0/J4OQyl0sTaGauTSbqR8BVHupOD5XtZhlJTQIBtC6wbFxw7Eq+eKtpb8zQRanUd64digdlQwB8JoLf6bOGsy2EPgxoafygoPK85c3ENVTdVSwxagNLmmqpaETfLfSgm0nm4c33PfrtdDcpSGYtJSDmL23wy3PajeBI+ERBgVE3HHMDS5qYXsDCwBCTYdGOloi1Tm+nYHExcSIfNYPmSCCbzCsNhq9sLJyFcI5yWvgg81YioqYCsW55b9IOL06vgFeiMALZIwLDdmifg9Ki7g266VgAtHdglq9Vf7kzPCEpGQ1Nlz0tdeQfBM6IuUwFrS6iJMeHgE2PW49Fz82NYClqaQpuDgdzz28+o50xvuOISQYQ1O3oZgTfWOGEEch0BlTkk1IriStZfklIkDSMeh6qW3Qa0kj/JLesDGQLuKPb41UKZTKalkNWvV4W1DopXKuPfuS6KOtubLac1fSbYi5LYSnwUvvmnf5I/FuaQO7FnMpJr/jNVWhzNiB5vHZcom1UKui8cSHUnTQiGpVkr/l+oxkd7Pj2nkKAZc6jL0uO0dMPrxlVOKvug4/MhbxjL5/amkLQxYz2g9I/5I6T/AKS7MpON/DpvdDIsMBV5befFRqs4qDBSps3oWEIOqJwggyHwVvTALwCQIKSfAkg94dxRuFv9TRcYEb5xKQ/zUNp5351npAYjLNJIsaDe9tkTzMAmPG+3dBZia4xjfq7Fp3PjQo29zG80krdoiq7KkCl6SJ2J9L7QyIDbJlIaDEpc9xMsD4FHBpU6dnhRjK1oxcXLaQn6LrxoeQmOc1RaHL2K2RmDxIc3bPUB6rTSY2KnS3aAcMv48XXEaQxhRTy1JkwYADjsxvPcFnLhafJ1yf1geY5FxXoWT1ALxzPV2hhBJA90hlOKqLPYTzUVzETQ9I8JTe0tyuZi4QmuSMiBmaDoU3rM2PTSZKQxGUpM5JWthRgt+LyQUxGLUR93B9MjDX1QW3HkBWtUyzFkrSK6V/20lLhCDHNnx4T57B8oNSOfRcNerRGAoVm1Fzr0rbZYB0p2/iGQYqSMjL+RdNHZE1RZnqL+w/oWpi6S99Ii0+I/7jqxPueyTijWPQj5qMnZbdgz9Qx3UrbXMD6cFM3ckdkPmH7SYmStQVxelcFhALmbhhMh8QW9FMBYsSHy71Aq7/ULkfe3gnhi4LzJOLvSYxYU62LhyL7vCvBNvdQJ6iEYAaczhLmEGs81zr0u1CbzHjktryR9MHC29gBjLrMMAofc/KFmQHSEIGFiooeGv8nY5NvHvz6dfTkS3jWOZbSepB9rEQb4LFuq1DTNk3xaj74Bgd5ocbdhL4AE1Z43JknCvcrey3dgfGWGeeMjdOwlkJe3pPDMbZ5J+Lk7fTUg+sjEIoT2Lsu6Apy2eYtpDSgbyTFN+lQW6obMKud4/luAtavCL4jxJim3yb5d5TvKsiJFO+jiXcUWifY9ZZI97lc/5ZC8yyFzADUZq6IH/ib7Y3g6vtiLQQE3z+jN8BVHelMtOKJ2JlRmtVxJzRMYnnNeodj3SFlIm8lgUdjfHi+mxidW23yAI3OK7GcQdozzVDok4zCi0BIixMYOZzfjfEOwfsBvqK/PLVTN+KoYUR7SH9Q9kxwDgkeQOAlIQzpUSDQAlu5pNSzsPlom2naiqvEKeZ2ItAnZoadwXsz2niTs8ZF9I+6vsj9KbEEfOiAiDI7rLcU8rLjwRG+bs+4mfbBNUG93ngxtkHES6p89nUMin7NO8Tr2w45uZntH3WNiU1SOpfR4agNsuH0cG+mgdn9M6+H/EAN1zmMSpIj5vHR5nN4+tGEyd///UgOXQKKLefFGOEKo5rre74II3gtP71SslzEX3Jpa1TQBhIHmhppy7NTFmAUT0vwyj/tax71zrKso5lHnNIEFpw7PP+43NOKxCvl8JnAMUCyM5LNly/0ZIOoBNRwj+RdkH/CpgXi/oyp7gkhA5Dd9AMY1uhcaeNPt24DvV4/KXei8ru1jqNnmAb4UJI69+MqY7zsAOsYTKL7ohuvrGQS8lt3X9PktqRO0dF3jxtQWSXaf7FaCTSqlgAlzwG2WhhTU7IaYgZfokaLvNa5tbnqrWA1pykFr3mtvbfVBjnsit+gjl8FmZZXTaj6nbNFmsO39jOtW5Ct5GiravGHHpPwfKBtrWTTm6B22mMDC7qvepvgqrF7n1ysp0rph9o6/jD3AgDPD3lXUPi2Qk8Fz2X7v8RXWVSFUVd1k5VurUBg015yUTjfpM8FtzBnlqQUE2pMQPYuKP4yfBVInY2DpIKu2nlVTOj1m1/RHFMonbDqoeU3hHIf07PcjQqmYJ4Fbdd0TDnkywCRivkC8z3V+PaBG8N4Kv2RVQ9BKv8SYKc7TS/GV6PFU31/e7jLss1rRVeviULosv4bpsmRr9LUUy/opQR4O//+yA77UVPla7pSoJPOCWYZG76EcCryQSFNMNMJOXTqWeWMjvLdznC7OkxDubTH04mDZlzJqE9Ufjr0ANJsbbACYgg43/vSfqeMjvWUh501Q020ZUxH4IhfDtJB9mETRq3tMsDwl2x1gd8E3ayOuOQyXoOSt/OGtHbj16dh6P6IE0Wt0jb8+HLXg9wH5m9rTnsZ7WViP+FzmbLr4oDF/+fCJK7+53ZD7IDwMs2eZnBPE3Jj1Ko9SqzX0MS2nDEKRV17VOP4FqXP6o9J9tO52xcnh2D6XDX6o0VUE7/pNLsOnMChlPpHLK8I5hMV+53LEDeI6EWtpzHpmyyO/bNabXr78tHFNrLohBpSBjOxxrT1tpuziioSkKJEOhD2kv2mOEpyH56IWe5hy9Z3Wlk0cTcIYJc5SOt+YARceBLbhXXpgSJhX0Yv9RzpiMQgMoEOtjiz4X36kngeU38Mfe1IIz9S9LZTtahWXGHeXCP4gX2IYsdpPcuiLQAOR9+g518c2Kpu+gT0s8DptYYEsnmpcwILNoCQTCF8Tr/8icQxtdYyOmYrALnlFbSfz2XTgzhhHDgwzQb1/VgKe5l6PMOfJZaUnymhaZSZ+B5AydrZTgxGbp8Mf/skrfgvW5ayGWrGvFnNmPQ6SRsSn6fasjU0obzyHhJo17j4aych53p2IbQfDGKdL0qT75bnz4P6exU1lHVledJg+/DBEfDb05S2OyFjKbsmKtd9uYUxgLyB4HmxyKQGa9yTszisjNv83gx2yiuc5lrNw+z3kje10JNwB027+YzlmzDqjzoZfScANMTzoWGHQXsCJC/+zC0puWYGk/pS6zYYYS2IQLCNp7a/Ri46YnbB+FBfBfbaD7oLkvl37zmpz/9se2rWfeT+RtebmVBzgQtJ/W2MfVH0ZbZbvZkXXgL/YwJqDtvcDHl2vhRt9RRrUVRAG5SjYXiaYlcet9ccV15cdCWqdM7E5X9VKwS19X6aKnJq3RQNocEO43nfga/KRibpxm7iGNpvQXYooWKAMNl7lXUExNu+g+e7qjnzWc6HMNs7VgSjYZ9xzfBodMnsVsadeZc3WwJh91pFeUuFa/Prk5ycDtDDwoNlKbGc/yC8D1DyNlbGk2Tn2QQbF74i5BJEfvpPYy1V+2HTki9PXqpsyvPagcW4yCZm/iwYqAqP7nQYN5rXMNzjTpkdNbt9bvLD9hQz0VC+XdB55iZ5I56KHBOXebHBmU8Y9TQkzhwGhHdUte407Z5BLQGfcNLHEqWlqDwSDC80mgyrHdjTKOEM7LQLX2VdE1cRoJoqbTuyZTxGh9dTaJn8YrzSPmLlWfWGjTtd20ynzGWF0SkhyRX6ifT3+aoQQ1FovbukeZtI7jkpkJINtxLsekO3VX1jkcbVuR41oRuPqWHnBfqlvYgNKh/+UdaN/9l+qI7Xd+YkOsQ/cpGxMg7KWiuzjA0d9dEXEC1roLVZ/3e4Wxx0MGTgCqB99s0vLe0v9OVF6E3UplQY8IlRkeTufkGOXzq1tx3Brxpdt84iVOZxlFyT29slE3cqZw31pAVpShtDT17r19JlFCZvPMnojaxNTaiMMxj2ewPx6WyGKXDCemb5agx6/EhqEhjficRdDoTD6b81YbiLk92AoFlMcKQ3dIsoRouwjKeY6wQ6L0ilhyWWYtY6FUGTR4jrfuLBHVkkAwwMZdj2XKMtdz9nJMkbN1hjULc8q13tj/vaLkDY8F7zCQMex6PNwD9+oTE9MCdZONUWcEp/usWCNMM5IBOSOOintkYHC7XFDV+O1YeuQT2o/K8ypnu/UVP+QtZ80vFUDz93QqTKKVVM8NOuOIN/p7a+89nILt11XByHjg0KAd8nIbtp3aWpRhqgitxfHYybqcyNCpEmx2lqWiW8QAAzsMbe4M/aKhtc7LyKq3kRtz3KCmjs3JHqsY0351bsbo1g0MdjE1NyVh3K+nQQRgEbLC71duMYRou/M/NYRTQ//yelbVyt/94GPBKR6I9rPdlhSoEkhBWMO2xhxZdH36fIj5c8T+8Y8XaXyZymRo+8wY9iMYegTK8kgs9iRWaoqt/saKoOf+Dq/u9EYYUR9eVK3PhGWZ34t+3bKdUAEhZ45ZpM03xJb4HJrUf4SrxDYJi+DBhn80pZmzIFuN3qQ0sHSZKj7+b9YLxoaeq8vLbIdPKFpnux6wzjhPR0hTI/Z2YSNEMC5dTU9GjaGvZhbmAX6L9YNqRiSyjgJap/DaBaWW3W9KyxtofSTPqsFfj/vu740Y5KPOatFSPoBVKO9cGtQ8ncQONjwKcabwpAgotzVY9g982zdGnvNuCcdjh7/80nS7gjYir4ELPRAKV9iBT4aHq/bSyCh0YD/6L8k7/aQlK88/OPHLtIhx8e/7u4edffYcidb511N8iT7R11gZoToBHVwQlx1KDmbtSCbVQvSUP3CKlHQc0hLByJfFjVxVUyJJXIcZ1dIp9LjECpqFxqZHxGBMUnL1HgbDs6i6PNo8lPb+/Tz15CDrIbNEn8Omip9ycrHZjtE1pdWrwX9Qz3hHe5O+3QaiFp6yD2PaXuYTScdbKLPkRLfDjdDVnjx8YTRuAA8cyL0HtP0pik7Td6pWedygpH/OEQA34zCRcKUZLUwKgd0Q7SmUq+S4iHsjXNSfeJgjFqi+eq7W1ukidyd5DDRBKtkyKZrcIoWQ03f4i7+Zq9bqTk6mG0Em0SNMWvxhteOtBfTT6ESmuiPrABvTRW6dVXp6C4L5Kbxc+rEod5cDEXvzOKFMfwu1g+SlWinRHUldEeTdDaRH9IH7nsAc+VEz9LQt4njk0JzEggFY7ZNWekINVBTZFSWpbayzg3x6KQ5aTcnFZgDFulik4YC0ZWGxulvxT4tnLWUh+IiWFrCTIdCthya1EFOUD7zPyuoMRD/DynCuJH6YvprRkcPo/a6vneant+gUTDfEhwVnrGTt2SsEowY5jYKwo2gYWR9vurbEFPsDYM5Gg2C191kWaT0ROWiGurwn9a0BYP8I0pfRyZs8jqMmN2oCJKR56RIOIWyPfGzEIaQMgshcDpipqUcs0Up4zty/4qL746aXbaeh1qGcp85ZoByrl5XU+YhW4SdRR8+6JDl8HhkX12LTFpuraTbyy7u1RFzovifgU+wFh8gc5hq7VBpOcuopClDpIqCCy/HwtiuLQYYxofr1fmE+oclYMMl7gl2JO/yCfWHlYKrfUyu9c7IdNVVvmVEnOmB4dldKxlO24Hc/8gDpu1C/GYk0NRfE64/y2DIhardEX33UTgRSHyuUwnbqXj3RflF7+BvWZ3MnhTRYNqgjb/cYP5qd7AZr44IhXDyOu3ler+1hBoeL/7qWjxF4snWmUdYw2MfRSYgCljZ4fDoReJkvY9t9sZrM/tTWpwujYz71FTecFdky1xtKfqHL/9kQYoszOTh3ZhCaFGQy6MNX4huFvXISX1js46ch9kjGwcI4T2WLtUyftf8t7Et6fI2jaI6skL+uiLnN/cVSiiUXVhLPT7LGK5e1yZtSd8hOpiRamB48hnsHZO77UiWcbh5F+c1P7XZfGOj/8g9RAenc/ZsmxZjPPXPQ8s/PmosBYLpdUAvjHVLigdSyI7OzxskOQMZx32S/pvGzZXTKO+EqeFzfAv8zh2zAVcaRbYL9QxR6Yz4t9okPvnWBui14dSSSutyIPwLCQa1hHN5m+rHKn2rxk6hJKJuQLRsBcg/3EBsRbncErwR0IYU4LzgipOkjFu6BzCP6hJp13wce8G2JSA6eARattc+pCJP9XpZdUGCRwCCBBnV41iZU5BZIZn61mwHBJmsEqItg48Tel29nK3h4IiipzmDRp9J0nhxMYg9DEXvmaluwYU41zNG4F6xSD1aOjAww+BJgjk7o0E5KEOsNOmOC/+YLMo1SjrRv1mkuAOvuty/6zflq9l2m0fDixl6L/J4v8j5Me+e+UdGsX0Bd1jyQRlXVH/odjs0initXKaK7lDNhU2psPjGgci0OAnSJXJ6pSc87x6HkggrBUZ1ERGcm3TLs6aCgtBZU1sKQVKG/6JPiy4oI9+3ETs18nXtLwE1F9z+aZLvzs546QXUz5XLnNGwi0gW1WmnIrKzhV+93m8L9+oHDRN694s8ccWMDD6L3r+odNnWkFxDR1320OacC4CHz8BVSsDI+HCOHfYGDAtpjAQ8j2zuSppdRZG70HTX7yZOVf1p+K2mgPsuq/iDKNiAyH8kagVpdbk7aEnKYwexX9fDkPeP6gh9ZJ+g/Sk3T17e7h1OWJ5ek9Y1tHV1acd6FkSACWiVOc1gMCgl+45OOgZT31HtHZlK4GHeEoSqCHjS/ufs18UqLTQeg95yth3N4EcZgKvCBj7Lu25/hxPE8a+gNhB5d8LjIn6SenQYQrn+AcTlvwuf0Alh0be48nYixdD1yzeH5k2VFLoVlgD+uDCwtVGmP1TDr2WV3Ctu2GSdMfI8zPwfjrlbjK/dTL4UQhMcTcjo3j26BH0xHin3yQ0+8+wBjFUEC87HmGj8DgKQ/qDZcsn+lWPUIGpfbEKeePK9E17dTqfr3A9TuAQq2SmM8u+puA1stJWGI59MIF7ADxE4kbhCJUJls6jGWCnjKxnlK3ZdwT/QC0Jzc4E+Zj+EYN83lQEaMwOoEq4UJTO0p3/yblsyK1D5k6OYhsuu4mc/0H6gYjEREODDynd++8gdKeB+lBM/heEr4y5ciafLhEDtrJAVsmQKdRQiJviDdORxYw+GOteB7Cxz5WhAdumeZ0TxdOrY8WGQPN2VTLcQE0+ECIrZll7qLh3oDOBKV9OVvagfe1yUVjEy5ESUboCR5NKP25ZvaG41niskGmGU617U5fbqBf1DzWobVygNdq0KFNbKx7iCKdezPQlH6szNuF0c2Xib9TQLpn0JHHldwH396Tp0P3mfj/sVKtT9xyFoAFtrr8Rj3LaB6PNAz3vAbmjsFjyKAufARDzBDLA9EgXikYC+vc/6nmw35QPGLAdpDw6zpXaTsLvx43UALdbnneBc0S+CpW0Z9vBAwQz7og+JXgJsH2jbtASvMikBTIlU3HD1VAoOnuMlUktoTZ09/TRxu7z+nIIVm1MmTGYgeUDJU/kukzgdJsnIACpjxJaF8JBwMtSK1gsuLcZlqW4t/clJMpNteTYhsUfqXkNjGNY42/dvst8lbehE/5hZgwH2dH0akr/sTMTK7nFWROr1TiuJJkXJN5tOX8di8w1IyDMYE0rDR9Tpu1dy0wSTwDI4Kmm4CiZFVpgEOZCz+sniNbtFyDEFXtd5RJT1OVU4h+/L+OVmi4SojD2GdrL1ocacozOT67cHwIc8Qen+W8V2f19cb3j0re5Xgmk4nZ1l3XqH9ULqH3REYhUTEdGWzcqHjzpv40o18dJSDLhBjXzUrzaEdqMwjWYFKSXTE/UOuHKSHeQXiqYVzekOthXKSkUl38/YCpF0RQCFB0ovNo+jOZ75PGz3egQB2HO/AGe0V3+dRd5JsE1TwXUy3UzRYIz916HXu0+Lsa6Ajf1eNLkUxpRrBcC1CZpSRjYHmvD1N99dS7wir4/JUQ3bxFp8niqeK8b7rEgDtIKlsHGUn9oMy/qQxqjgBj/ss9lDPCjUdwY9Pk9FT1d7fB8n8TqIX6Robyd6fUXL/c+G4coIOsap1v/w6fr0igrNPn1cXKZ1YB+mKohra628QwKSTY+QnDIUegM7xZo6pNag7qHvOofDVwOBII5U49Ql5BMKjmo+4yPqyoJmXynsmGlPX3G/Ory4bXxOFJRSbsOkKO5FG46yrppN8Ccv4WMV9MM5u6CcJN5lmSt4WvKHC+1CW8LyMwligs7VwfsvJfTMTgE699qLleiDv0PoV2EJ0tbVfoqDNrABE00TnCF0TBlNyf7cuitdFtAYqvkl3EhTGo6J2o3bk3iz0u+5X0N5vrDh7q2rtanxT3BepkIgOZF6zHr6e9oQ556mVvRBbg/4SiQbuPZM2w1/hDPMvpPe4zI5NpizfCEcb30LL/3n4SkwYcZntsx5h7szX/TDP9+qMPVJkG72VXoCyfd8A07PQKfnLey4UDxEzOMpZe/oCkuq6KgP09Tk1QHpFBpnLadl5Qftyv1BlOxmaU5S1A2UZwFEThcWSGJYokd76JoPdQLB6as3KhCf+NyY9L9/lYKrL5rKl8UNfsxUoCkfijtgDO9KIYZc+FBfOxNWzYW7NIRGUacjH7+v6NPAczdlBYKEWlnFIRU8U10qD1TBemELTOHsav2UUbsY5HwfMHJ4LGtpQhjdV9ucadziFp+L3wuImgRYXgUsM7JcW6E2vVG2Eyae0eVl2a9/IiojM7DNpNXW6Pe9Voml5ek1k829NQc/5YzAKkehByBw8i/vju+7xQBwlzJPkz/oL9o1lZB2M3stFwa4Xl3RPFfmytW97nq6qPueHtyOg6FUM3P/jsyJSlWXKDUfotlROvWQG92OY3SzZ7IgCuMU/q7vBW5b12fFfMKa8mBdoiCzcq/vXT3p7bh2APn6Yo5npcCsGQI5OI/4qC6yglS61jUcwmlFqDa8W5DMOZ2fo6XKa/2PB6EMazQKg3loq66Cf2X8v+YP2iqlaKHEIcJCEgSC5jfKdLPHPx6aScBnPe+WT/pOqZK3/SeEQeU3f4rASZsIS8obPGxQl+DfjoSrS7NJJ4XnJkojteATPuHEc60vhFyb8tDjrR/8dFS+CNAs43UPvuBji/KxmQi7PlxycXlgm0SZy+Zfy3ceLdywlwgzwttNJw1hTvwI8oVmn0H6tEy5W1JNi1rw0qyflIFqUxzqYp7MOKh35EAHxiWAE++PvmuX4U/O0c/9T5SbHBTn7Gyi1oRkNxmsEFblsY1Xx3Gdv+akE8xR7MP7bzT/84X/i6WEmJowzZA0QYHhtCcJIRbJJMUM6bnF6yjzZgWIubNQ1jsmLbzh02Y+SoB5SJTB1rlhR9YTkHRYm4E8BdUYYyddhgpA8RtAKSMepfQrMbL/8uliCie0FNRoyT9vAVZsqCUYOSLfo0s8ySS2/jsDq3AttYs1JjWBc6RUzGhHcUhjzzxUC4G+DGBG/0nypwL3Blp97l93W4QcdsV57G4qq5Acgl2gD0uRBZnwCjskq9vMf6wN72ZegFUa8oLUgcxCK5IMPUb166iFyFhrYQF0J0jsSzfdgr2ezzw/98rRXIEGaP8zojJFpiOscM8hrGRuB98D9SWM3pdo8WjxaPRu9vVD8tlGezOF65g0/5gNUGBuSro/GqtHTmjONUrcX5w6KvVM3/6o50IJ0fDC5q4qYKUrokHwdDKchPprUZ9XLZVykrXIwE+Z18P7pvV2iMKUOY+WEtrvIjdPylSJcVWhxf2kLn8NeKXQyBXBl8/aWANZlwdDf3uWZuL5IQApe3U70w8saDSC4wv0iEdbaoriHjE32WNNhPDZKySWrBmqR7rS1MvpnR0uirwH9ZvQMQ5SdFm4tv80PJekAI81VHxKkhg99qRj5bH1nBo42+eZDcu51MsF65c6dvLFnmsYSrjwVtcyn0avZsELRyM6tROy/0VxD2YLqmzNs93qwp2qCXh5SYsTfyuPeB/E7dQN4gaBqa215r/UKzRFLyh1nZTTujAdQ7hfsERzMqJoLnCgwW8veu+ElXBXqIxJgEqElTLeREtyZM6+AEuWHr8bsti+cvYRcV34leSTNHokd4QwnEPwODbS4ugvmMEVikDz/DOhKsv75r9LktCXXYo8XD6G5Q5NX6+rnflU8YgXB4ehNzQeqXlDbGi8hh/k4i30lx2ZAL9dXEjgcnsVFZwJnrYei+w/RB0zgciLRqeMlbFwk20YWTbl6Et2oObxqEfz89ByAmkYql/dAycq1sPryli00WiLDPH7vC0ACym3olLbuGe2At0ZukjHVXFnmFubPG5CgWvHsXbsdoOEh2p6e7qV/CZ6xV/Kamm2dI0Bpsnlh9L1cGTmeMQ5KuYZTeWnnD6YQFbN2vAQhTZD9kuP8ojMy8hyzNyItzBXQixhq9oFHcSMCybq7gYV6E9seRjqOsGgbmKJCbKad4XX1FheCcnUlW6A2WC2s2CtSZcxxx9yWXKR40bf2RpXBw4DQhVgkOyRCZ4ukbNZOYdd9WeSOANuTzHQJzBq8KCTMumfq06IZkI9u9N2lNk94d2Mp8x0K65kISKmkIKIr82czXMOFqAl7A1kgyxueRmKKicjI4ZfEturWW/Dtl2nHQfrQntYaTv21qvIRMb8LLH2qpnUIRAtXDELZi8/1BBPcx9qb0gYq7ilF5FJPPtc+KpZRbtf93GzWpQA2QON+8aKa/C80b9gUaNi5ajNIsBu8Fkyat/nGld1h5N+HQFp9AI7a9fgzU91BkrJoCTAXEwjbkd/xe0BIhxjKYVmXz+7iOo5MXUBNWzi+aTBawYlkdmS3ol3mw0JFNy5Jc+PJiY58+f31/5rBmq0tABFGBkdKgOy9WoZAQ+iwIRPJQzdu3WpYYGpyb5IjW2ou4kxaFo8bP16t077IgseYl0omq4/r+PBjHO2w5uvjiDQQqDTqFc97A83qs9YGRze68F001yXkA3phIkp6TkHRnd9Os8NtE/I35XouUtY3FPFir1+uEFSHvHSk6dDPFNZK8eyEVpmxQlRp92FmDajGE28bYHuZgWgzY9DdR0TqxSkWqgmzoSRQKl8qUdG7ZjBi9pklTDzn7k24BYxvOxWzc6AwwqZyU7NbQl8lCwF4czqo+wHSkQgNqHWTLCbtqUndLPH9uXlc3R1bxsNn70IHtu27BtDDCz5OT7tjN01EkAjr/hR49GyA2c5A540ncocfPeCgCRLK1ck4iFXfKN4xdefgYcUusg2DRzix02npEFke8O8pL1DeKY9n8WKB1zadOyoGj8aOKuOVTqZ03vNSoHga5d8Xa5DtvFYTIGmSmVPL1vv7+PuVY/qra07uENWBaC6416TX59rFKAKerwLNAMiARvf2yQJ73C0W6bdfgGY9Eh3q87sk+C/I3cV6ldwdmV3N4m+nk82fLY4SG/w1INl+4WwRruBQx01PC4h+p+7e1ERDZZA9my07K1oI3zNz/tebJOGotN0lu5z/TpG0nmt5BGZ40R5tcrAQUm8Bll7a1YN3fjwBksELsrRK2+qxrfJJhXbY0QJamPUKc9rjq5aDL1ZsyNkoDYol3nU/cu0H+PFP3Vudx5MzRbPzk7dVcCtroDWusxB3sqkVZNKypuWaBk+1WOao6Ad5syH6UvTblawBrfu0qJcRqh9vHJzwcN6lPdXjeVs3TKs6Hxx0Quwv78U4B9WbNs7/BpgA3cjkH7ZWnzBxeCgAE4xZE8Yh8zwheuAQktAr19GWqEYWcH2paYHW708PLfh21ZFqZQTcSAkW+w6q/X4ewtrsr3O7ydaHRsn+ybYQt2GX+lHHk85csWczvdvU55m5rO2JOxQ2mvS+X/1o7nmFzRSK0aatjPMHYlhvqpU6qXwl/pF9MN6E9/1tZ08e+GP8g11b5MEC3EHUXlrz5EmDVVQcF6dzpdCLpYBbvuJhVNDCu7pr6K4hoaDPBMloMfA8WPMGBshtK/NIRymqSEZ7GgGLUb1zpOhTGbkFKYlCgdROlxm/QZloMiV0Foh+uCVdigMUeUuxPvjMK3Iei+HMNl+GsDqB2azlx+CtNLfsVRE0f+V6/xsFu8xZ33l+YKREFbAaYY4zrwE9fIE1izB9QF8tUrZ9NjDhVti0sD0ChdoAqc4uvscIDrt7Z/KboSb+geab2nJSx7SJWdGWfNTRtk18r/zW8NtQwS2BU29Nw/L3Cn7DfHb/LhOq8N7VHM8P5Fj8d7m5tIhCh/IRBfyGBGzU4N4t5WjJgbjDOJwHXC+q7+zVIsllp6Q7CXhM1vlmUAtiKeHkCXj68k8SVlnodGZn5ExMgtG98sVrDE61o3Gp7tPU4V8Hu+quyfwHYcHzoNHt97oQ+OqXWA9HLDzvnoWNUMDWHR14ERiCbGT5uFhWGH45lT4GpddEqKLRy/4XM19PVzRYPyJ04hYlg3cncPSMH1nFTwZPkC7oftWg9KWLnPeFS5j1T/C8ojo+jeYFpjpLZBWfDxmrhpWoliOL1PoBPQXH8XJBpqTZ81ZY2P49OtS/Y01iKPta2vhpVLimacst18VT7rh01J3eLDfoQYQD6W31hB4B4TnQUAaQUio3dsiSW74YXFDGsuTL1EPzHCTPpcNgQIilc6sknu4vA6PCi5FltRgTL2kGnaxml7EBuVaZrUYFT7CJ3ZCURh6Y1L5JW7Baqzp0Ud1O6e8/y7FhbxRWs+ZiDU11rv7fo4n4LV21AaSGWwAOCajG4AXTK8VZ4OcSu8KnC9gSIS4nRvhjsGUBPQf8G1YZkheAkxZNFT9YEKg5bhf9IPt38X3zWsYwHdDfGvs/or33NVUkDZ1ZFm2G+iQqbM5PrTpZUrDMaUbcc5bJBV+L2XbJ6QSUZln9vZwDYJryLDAZ9OutmBgV9pPeI6WSoxgdhdcM+nwiBJXJS9GOy9Eh9qfYEKc5lmgVtbeE41CxUk4WIS5J10rgz/GNCctlaXgjyYzNvOwTxEXBr9SEgFX/WiueXzmILUpuNle323DD+QTxAoC8s7tZCGOxl8bh512r9lgTS82GNDJ5BrXqG6xlm7RZ70vpPNr1w3F7qAxaH8np/zcVSawvJCXnRRuZRpY3kZ91tFZJl75cNaM8O19y2y0LAXNib0TPv4CtXvIEFIcZVR5im8esmMGOSWrJwBgaAHcFtOirUVSmSjVxlFsAFjqabfwaXt2qoWkb5emJd5rcG5Y4RxiVOvuIC6viIbTV9sEEWtKsL+5FIAXR3L5WK01nhNgpKJ3WGxKth5nmZQBVSZbfGEHiG9RMY85H2l+fgMUnbMMjLVwL9K44xscgEK+79Lhb3EE+W6Lb6LV59QWAA0p4SfgzBm6t8T3qfkKAnMQe1e5S1cTqCkctKGVrBYQWpscCivmWZaPpn3+kx9mNzCo5njetSsIk4864EgSDhTa6KmuOYWu4+Luj5UYAYaLRUfT0hX3r/UHCDTTDofFxnr+r4QT+Mj3+nMKOYAgcXC7wbai5U7sYMiAsXF3hRGwBdsG3I2r9McOAx180Pn6kXFmpL+VQ7J5kILqCmZysp1ciq1ZenIQWpFD7/wK6/Ar+yn1ah7a/5hZmlGWSLbcTNStWx7AEZ07vaDp57W9/we9OdnmUAA6SVb8DROfqOsuuOOWo05JHRfAhg04hk+wah7qSkTUmivduG2eilDCZmiewK9u/DEZmuAIwQfM4w/Yd/cyUO2+g4DQOVA47ENAOkJ7DeLvnZAq05Aip2pX4LMA9XtnIblnNq3uDM9FMDWHZzV3g5Dcj9UQvMWqrqCqQQmrjnYLbfR9YYlDfNN+kbjfYrCAeMy0fz1OE4vX3Mu5SRzW4AL+/QWmEy1jSTLzE5+KG3aN3oi9erq7IOI+qhEQylVVjUYI6Vm813XJ2wgYvRyvjkls7p34JiRGHMJCbPMl6CylXV54vD47KgRQ4GZPz8UVuiF23AGacwm10JyUoGBlDRSSZlA8W1b8gGzMPT/sYJMo6r0NZjkZ4joq5vVuFvF7fYK0VmVr8JHoWt3l3poWaje9IwlBfpimX94gL92Kc+92vlZp/ZN8kQT55vbEmNpCC68D0zPye4lwpvVgcSjoOZRUc+mUBJ0HGcYh8HUM6HBBXDZ5+lcdUXQQAydinrHhdJaZyuEXddTtFkF10JBqiglqtruKFK8n9zHH/0gDIwtNwfMi47Vpe5+FlOOfJRgewjV2KZqBOlgYkbuYl6wlhwhbNGf7yPAUY6cXOD17lTatf6nQc0is5oKEm5c2qFFO76utVnQTOTAcBFw11cXUZr+UT8daFW8mricVfI98YguEdAX34BUChZZOpeMoMlRjDQX0K6ZAiPzRaOA1M/WvWc/rXO5tzWjPtpjLUmLRI7RMDMGEsuh7WPCb6NhaPrTyj4sLEuTJ7tKD7XonXf42ZRkQBOZPdMtOZr9BkWpT4mP5RkeGqM6Gy+YsuxS3nK12hMnRtNu4cwzmiK9MY3cj4T4T/3VimAAyoM1Bg346XGf6vhGkWL8z7B2yATOtYpZCM5DMVB/K6zeFJgqgqkMJafPN7LfODzoN8myMOm4ttnszyNpz4sFw3pThlTCkwvE6weDM9lGKk9vR07nmPQHn5h7921N5h59P+vOd0BOQ5L/vmDSIqBOyOVergA035Bkk100I1/YKshce7DKjn19A79P8WjdpT4yksLVfamdN4Gknl7hbXYV08rswuKRd1o/H1SY6S8r6WlfLzMGXdr8s2fZALz3+wQBxCQoMZFQ8jMo6FnL1mIVZkaXqUws51sDJoYNUU2JXTLsHgjppWA0jTut1BdLI+DBM9KojdkuKP9/vdM2BP/HvEpzyI5euGQyfGKs60e8guFhjAQ7GwRY5CD3FjPuh8FgXpzCd6ZLvwWJL4nKhOI7Qyzx1rITK9b9yF+2PfuYLVnePnb+YmcVH/3JVZ4G79oAuEWgbSPLQhgdgqUldyIw1Sxri13I1a0BcXnWqWncRTqUf0YGC+9B6xUg4vRzsuRwyoBnkWpOmCUikIAXA4Nxt0avy1IS+iA3tljsNv43WVSSYE8tnls0gk6jMdhcyAVN04hCDpHSO/Xdlstt71lik23HB/oCT1DApumsLq+/l08YwHMfXJDsZu20gwAg2NqYLmLHM+jTfy1mVBdzmjz56lmlLW3x2byvBy0s/hY8fVykrFm7p1TfV7/cYgqVeiPC9z4g7Rjl28gZfNGOMb6Xrs+tfvpwe/kedYrj1wHOdMyMVNSwKsjB1/MfpWdoevEXnF+uEJ686ZElqjKXZ/BghA2xUKQiIi+fkW+xlIWF8/C/g8IuFMc6ZtHrREOcQz7dprZvAUhZC2hbtTlcleg8AJjqivdPErdgVEENOQ7QcW8D+VP/MVF6YKr+I4ZskBzPGyPBi3UJYVeGW4p6tsrsFcnVIuQoPZab6aV+LNoTPbR8UWIHX8jOcGG7zZUz5uh+NzogmIB7voTA2GK6UlxkCBpVMFvIT4L/4dMEMyftmPDGc5vYJ8sBw3W6LhPxHEeONOyw2FVeap7cz/CkKVsVUVtidhWb371852NyDOyIN0FyTh99G5M6BtvzOkQw8vCnqUrcjg+RTw/wiJHC6IrT5LZtowI31LiuXzT53Sw4UvzEwu5dXWFzjo1QZurfPrBzsDgsabC1XVSd6xF8wYWST65ikIRATsrf0p1pxb8ZUzT20pMlDRVhtG7ZRMz7nSRD3/VZiUs1jTD1k6ZcjIyfcpVy4atRWsB29aaYFJIYP7QWJ7QK/2NF4mIwQpM3tjp5gXUc+tzO9e5OEME0KPffwQ0aQ+H+0Ux9SfbiGcnGwiQ3LWh+b8ifoiO/JYNGQMEulabu6cz1NIVPiv/TXtDMcECsFFk08hSqHt7cyEdzI8Aycv3ISWNXuAQY7nikU66GUImZcVJ3hMKgTYO+0ZLsDOkb5hSIXM2i3LrBTj3pxvyCk8+NwuZ3gv5x0hsk1gP+vwXHNsPtHDWnbwIhs92/KSMlmvpSDoezK4mjVf4mIwS5Fj6RNJ86GvF7TQiEydDwZE80vU0b6eL/Pu9eelfTV1C3RDblSrsEK9FKah8fmxoB7RBV9wdjgP5tfbQi0cCucQeVnUuLDnGQsNBCmDT57dKvVeD2iZQ+uXpQalc6wK+1Ndz2Bjrgowb6Cw1yHKi9gvVUYyFPfjOrgNSfosoagKYjT/eRX5QRkpiSarq0cq8jZdWqKTNtdedbPSgC3dQjVNBl3XTbWZi87PwH9AreDAtdwu+/ZZ6YKF59lSTpJLdrMPAunvr3pjV3JUGpVp48RKu4caYw5V51/lITlLhJXSNmzd5A2k7TPg3dZkwiSi8z9JsDFSJhtS84Ch+VRk8w4EWn+YnysACFVy+szVD+VXm/qZ+4VIV4P4VGjY7drOB8QOWjBXI0Kp2LJXD11ABUa0dIh3mV9McOKuWuu15dCfd6VhMaidG9VlSIQe5EalVprasaXg2l10690VvShQloYLDHUwuBjshA5vKF1SnvLzI3m2R14mJ2KVzkiQn/i8iTY1YHLuz5poEIdr0qykZC8FPCVGXWgaxI/LJhYXV+G6Kl5KO4Lf0UCFNOCXjuDSOsQJOhCcJr4m5YMSAV7vLaAE9elMjrblHCXRJ3/RERUj3opJZPCpU1YywFR1fSg1y+4c6gydpIUOFYGrxVtlSgUWWalHLupBXQD6o2NfNWv5pI+KLXp9Xb8FWj9Pr4RoeyzxFjBQa0o24c8pylr4IP7nyMGJ+gVO65g6dXP9/khUzaZv4B/iPuv6tdrJ8R5gq8iWX4Oj0g1abWIkbT2dNx1opj7yPHmFfbPXePlmHHXfOxPJEUI315qlzlWIAcf5XyHDWRu0+nHf4tdgtwIc/wQajSRuiaG0hJIUiuIhGDY+Ug2bK+hFkZ2S2YG8nJrDv+/87HZakXHsALz4ADW5tnUYuIFeo+qK1tgi2E6iHF4zk8b9z03XKuqUTBmfC87IUlZOY7XEDMn06uFjmvH9Jevyylw9OgUFE78ewqvbgFMvCdncV070WxSgwFdK3Q4+wqg5Xuu1wljPO6q73/foSIi8Wf/G5qKxBoTL9Mwqjj34x1/yYhG1MdhUY7dF/EyNfsUlO9CzZSkLGhk5kM/XEyVlFa5IHFqCdBohsrqs2A5PfT4qcWrD0wMeiFFSPlgQYsTJiK8xGtUcl4mg0P+FFQIx2FQwjBWRhByYASiXSlJiKn1lgLvaBk153Zt0o0YblB+YyGrwccu4mTDO0LfdCsvS6i9cD23YscWgkmVIM9/D60U7I7ximM5ZO/7HFYUDNgnlBW83IjTSuhVe02IFt4+g+DvXyl53nXobl0VXmi7eE+dJOvMQGnmWcPWnX306fHcKQYr02aoym93CPorbXQE5yy3dXD5UG4m1l2IZoMnaewLGiWq/Eex53zXhYF6NIeUWGpZB4c4daP9xXOSzQV/lB7TNrwLi/6QXZ5Svob4aeD8RbS27fSOm3hBG8iNRGGsTkNJa25elxtUDKaB0d0pBuFdpsuVQw/Ijx/71s2g+edD61q0JSm7PhEFGqTLNW8gSGZopj+pqVcRnUi2hCyz7USTrPiM/Uj/bgJa3Hbx1+Li/SHUMH4Bg3ASP2HSWdGeqwmaZfo7jft/k/R5vRIa30qMaXIe74KByBBBGOohK053mXiLIl8a8u6Vkq7CtXVzn/Boxa1PkRLMWjYzmVZjyoqgWcyDjUvBHnDRmbuzdchWAhzyt1X3yiY1zq8JZjJvp2jzLVYNuQ9eLlNtpK1sgp7pQfYvyCCmF1c8zTCQjNBTt0S6/S/jNgkBZXjaqeIyXk44ufIHIL9IxcbeIuV7IvBuahHijnstbA9jQRYPyeZsMm/mTi3LhHOyMEhD0IN2KyRDeCQ7wes/WwUtzKy83nRROFxh/x4MNR0+IAWKG2jdYs8Fz2dpvXUcaEsKDq/YmJoi4o4fJYCdoyOpEsUgrCVHjfq5bpWnG6YVapzVsFQHideKOXJyIcgls+lqigGEEcUqnDgUn6OPdA/SGrNHJMftQUWSA+EpuqXbUO1/6tKb3MwQrXYdhqIem7Sth0QMnaGU8uSYbpc2WlloZHZga/pXI8GNCU6RJ2pmKzQzZeyQROLfRCkpISjLAbO8jVof+ObMvR1dhoSDbU8OIEa3W421wcmP0Ic1t5A+jKoP/d8UHeuNeUgrTflMUPXw6VdvK1knhaYm9u0ikAvYVVWlaU9lFUCO17DBD6Jvbs2ZpJgwVlI3+pv3hTrBFHaT1MLnX/Ea1o3D3l75+Ivo7Pk5wTkBFrZUyJosbGkA+U5YZxEEBRKtOetOeD26b/+4N5wcMLZVqDsRGtbgEGu8dyHg/ROCOOMTSsWW43Wzp8DzeuobKOnfFGQl6LRITGK6B3BZlt2eXFK7XKToFBqojKd2f0fDQJF5C32eD/TJF9DYAVkYKiPGRcU1JvHR0gby52tkaO3AWrmyAzw8j6jYYqJldzZhSbiaTnsrR7Ou+SiUKTX2YC9axaB6WIpl3L9ReGjz2Pk2nzvZtUXTACw/Elu7w3LXaWioGsjVfjXdWzYx0qmu0NCH5GIlCQ6ce1hq9/Yi7xWzEBl6d9BI5FEwRUtx3Vp98REW0Tgbay6ObnYmd1H/3DUhkso3C2aRfZZCZjPAX44gFNlk0seF6lK6UopLWvJKXx/qibKMw3nEWp9Xgv9ezyWi4ewADw2lpZPxcEG1y5fVFe8it8Xvt7Z3ovF2Wn0haKxJgULfbjFnhD9n5JCDJMlnhMGd5JX2Ua+MPdY4UQpijI2AsMtsOhSY0BJk1NXG/IbENYNtqzYuTkR/oqdos518HsDgXvOkjN1bbnqo2Y4nW7yTDBN+Ol7XGwdH3Wl0KGYyfCHEzQGKgvLrOQy+/sHL7nWg1VfqFY/UZhR1079ivpOSPh+CWzvTTcSrKw1A+MqQ5HTF+HaeLqQlmmd9cHmns1jVIwZIKQUg7af9Jr4AeuxjzwVqfpNV6YlfhNmIXr/0sYdDy3o6xnCI1rTiZK32bhydWQ86klw57ykcEJuX3NpvvGQisMgbKcf2qWpP/MaJDIPM3V2mR7zdRO8XFgBs6s3x+ESoq1FrL+rAqs1uVWgSxWoI33lljze9nJwlnS8hn6GuBrBf7Z2re2DNvZ0WvIbfpw8EUHvavPwgsgnSTRmVIhfus9Ose/M8P9lehNWapdSeont3tcCCPn2JM5pEBBz3PDpNIx9NCpko0veI4OrTEAumN/zYE44wVhGZN8kPOW1wZxMnZrKEVw77SYUAL9fen/UYr8tNCQ+QfFKqbvaDBLnqishGaFIJHt/dUvb5AHNGb9n0RaPmW3GirhTXvgwytMAGPhUnT7rjLkI3kFgsAxN6WJemXgvTVIZLwtlFGkUrM+59hrdT+zneNhq1IvRoL55zFqq1dn1frPSLGFXBN7Py5sNuI55RJ9sN/PULQ0iuZqrt2Z9Lq05fo0DD9R9KvCyUiczRejfGcwYEqICzRW+A09st9kN+z/TOHALXUEgY4czqR0PnyI9elTqKt/5lraCdih60Ec2ocv/e8Pw8tB9vYmZIkhxJCt22mIqOxF1BYqTZo7UiEcX9BURdkQ0KgWdA5Uv69njOhtsjMwYwxKt1Lxm5PeCz6jiZ/fZ9ICXoyv44UykNr2m91Ys/p81YFd4XA2DQJBl8YVOizve2lLCRmnL/r7t7NqOhHnT5iUNQrO/dPjUF6xqJK64ycHcVs5pdQEK6MMMeT9kbDo6aGSu4VPrIsl9sXK08JLsLFoYaqalD1D2N8o8RZZ25krVbNi6yTqs8M0RQN1IIIPf6woZMkRb3k3vnCpGYe9gZDmFKp2CjddFb9XqztctSLZN0A9yXkbuBITLu18GPes8k8f75zCG1C8EICm6IMXqb85UDmamT2pQ6tRqenZVkjRCtv7Nb2llogTSnvoVff/yGah/JhR2Ro1rLaSuy4aojuJcvMNgMSGI8H28h9G+HzNimlJnDxJ44c/6E2g+TqQq67ohs8UDAiufZBuNlRHCGD5JzUwsSlouSxhrrX364vHeN2SKs72Xi8EObTipStk6SvXix0lp0qkia5GHQ/wxojEla2wFRltr2bdNoq1CiwMJykNXkEm1TikkxOgbSzgzp2JjI7AVVggF2wG/89aCBmb0SqSBjTIXERv0HCx1cKI13ZylrzvQ55FDyB1Oc7E1tLqunkqi3EVgOQa7555EywmfrWv/GpYJRD6sAdd7iTrigJUNJJtnlo0P9in4sttUKlx6i4SrOwylX2178t618Te/UgnHjUANhY5GuBjSn8YdB0wZidCuC/LNk+UiCat3FUSaXubCSza9vCplrmXopl/XPSGfsOGGeAK1/gFYVJ+RWAEuIs3dah1Ly7cnI621TLwnmkt/jSebviMuxHSio+V4bsWL9hPXN3fkexRQ3WHbzEAiXU5jLVzP5jvdUNrg4WgIyhyzS0sjwRoYWPKWTWdjisnYtjqKXfKj7CLjmjZyhiiiESgtkYML5gfVMx42MRkxsFeNjmESMsj/bK1YiBlmgtlvHGARyDDwJeT/8i0fU/cpn28cvlNKJCxFhNMKvvxsLAj+O0o9xRbI3ybBFttZKBS7JzmB9sKAKVBelXGgnDqy9HoAu+sv4yYKnI/GaNIJJ1Q7nTcVVdTyNBzvY+BBR1CQRsxUXoLJw1D8VPAt92cmqkrV1/91pJzZ0KQ9quBhcnskRm9gFmHkHwoyItqpk5hXvtLXC9lK5CyJoV5yfoiPH5fy57TV/L6LHLsGc2XDB+z12RM3Fs/M8b6ciW9iHeSRe7i8c7X11EeBePcJSfbfWWNpBCGUDeSCTEBjIyBF63m68cPZqTvFPiclx+gjFr3uesBwp5jjIsPHXepK+MjVRW3PGTs6W4UNuAqmNglgkFS4X/9A/GYr3oGxgcG9uhpjP3xUjnPPZb/84zLmM1SOYy3slkY2isgt5zkq/L90ATF3x26GuQ4H2HDCgbW1fUhx9TiNINy5p0Dspe3z97ESVYOEcgBkzCjLn1xT4It5RbmPpNGeeaBaXdtwNJXbzSkKIZ3AzphkVae1N4eggowNhue6YlRpLi4M4DpPjpP57wUYRiEaJXcSQnNyTFqTe81iWvExqNeXX3vWzFXZkNqSHej+/Zccphnd9SRBsdQd0DLNp0hsnsB2S/e+RH6TZtSUqvhOVzQRc29BNcFaCjS4yfe8Qt4JxByerxIyxLLYkrnUypqEITSOkE5FEDSR9eM/qOjjLRRf60/rmtGqgqnqe9G0R4q1JuaTIFjp3BGVxAToONwrJGrh2/fVPO/iNP0YexjEDkj+OeLMwKF7E7s0BQ8RDDr+He2KVSOI2Y5d9xu9RSqxJPdMSCSJ7UO3xtb0T29Oj74Koqn+7jMqkgEv5xCl5jmx7DXSwVtgUZ1ZSouwgTT4F9g7bCOWW5VcyZjX0rGtJqOOC8K6yCxewsd/86DYBZgk43FL/2cfFXfI7LPwSmwIeTzygYGWvseGR8IJwwsNWL5/y0Uf9gfgky/z6fhcyoHb+tRLSf/Pr6D/wdOnPLTnCGIjQuIDIgRx8XfZPBMnugFtp0mlp6wSKAIX5eOWzqA5sAaUY7izSoVaslcFyA8WPl8/90r9k6rt0qAUnUIRvPP9SJhagIFbT6MqbHU9eCo1nzueHiOKOMRAvkV6VImIYbGGGqCboYziLhtIzfVNdOQKZm2QGTRjnf2CiaaIlF/g6Nt/foU+YIXH6UR9iOVOPKDABmT+zvFsfm3BYG17RI11QTrZA03tVlZ6nCHOUM5oIHzbMTfg+ED7zfU9Fe0rjCvd3IXUlZod7ttyGxg9FEjWqVyaIY8mfCs4GL1+q/d/yvlPFLXjlWS+nqroK8EaDoXVmcFTvSMY5sbkmeJunWYk4vezBSm/a3F7Ou1mP93/r7QzB+v9AyKTZW90RE+yRmn0bG1GjZA+Eyamzgv9P5UbM1dCMQEXj1dDF+Zms3oTd41Sfs2Azy9SjGuFMiBiWCIoDO+gOWW5oDwUi42aUCRZmbfDWinKxAtwJ8byFrjZWgwNrQciG6xYkrUAb53tvHwcOnDS/LB6xUU9KEac5Y6jY6h2blcOcMz1JpLWwVqNyvNzf2bGc+qI/zNxzTMqJ9Qqh2AgwEDeq4YjNsf4DgSoaWfOtkAUi/gbpiFDZO9KccrFvuObIUN5ISJfm/BPieYhGhmnD1PR4PrvOBNybI7Hs2HBty/sNet3fQOUZNMXvL2C5ZLYIlIXMyilTeRIgvjcxlyxl1URUGP1Fzg7V79xg2uHGLz3bhjOMwzO4JjdwSdEcbI2WIok7a9ypZ/pbvHKCVYuZf97FgUooFRkLR3YcgvdhuYZPCwoB551LRBzf6d22AnQ1tjDgh/j2CdLN4SpuUXEsCP5V7UCc0ak+ZdvlsA6pzXaYPbnakhNwmgZnTfzE6DVYGEbwDQaMkm4MHXipWWYxMTDUKDDnO0yDCftEQ4Vo226Y5E/DZqXblQnNCVmlZuILbEllC2SpRqdiA4OxOgzJrdiZDIoY8Uzj3490k21dWkq6P2ayC7N+5RrnM1w5xZOEGX+GR4XrtbHfz5abnTOHv4DM5fwCZBLHYLRUrddRleth+ILQOVUVPGzvGgX7ZUw6fF6SxT+wA6T19MoH6TYyezhDp/YklMO5+WZqFDHSOKXxVBVIT0n0cmwDfoXELP38jE5lDAFunoItPjoWYjYRVnvtdlwin2GmAyGtkfvvQ5gz0mHfV9sMzQ9fmG7ZspggBvMWjdgJ0LELMG03hGFI+1NahNy+aZpvciBZGIDvge08vW51unP1YtWZ+dP3i1zIKSgpf4wznvglGFuTTXOaYB6EnY462zrZh2SKML591SS1BalLJS8/qU0x7Wtf8YyBCvnLGSELCVJMGD/oTXG6TUGHIF8kEpBDw8yb465xGNZsrL2Kc7x3vF5s2k4UF+eBD3/+iZ6SiLBKU6QYEOraR7RFDY5H7R9KND7KpWBryViLJnDdYdyYEbmX42coe7agiotzV3i9O7A/tjPBy6Qw8cAQHkY86L0c1Mb8Cv7aIXo2IzPqE5egaj1O0PVsZ0wMfCVsJ0R7tZZ2Vqp8DYKa7nPAldfQxTmC9fawTdwkJmEhYQNVJBboqx85VruhUDocXqvvt5opL5gjsmZvAgkfPyxGF2vawdb0gsm4CMVvcbqIRVz1rZscIPuXIISD90Jl//V75Jj/cFMQNPBo2MXqMlm0y/f7mSO5+6sbexYlru94JTaKuz96MyHs6nYCIPsjmoVjD5eQushZ6uvCiwanv6Ao84L9KdkCwpRup6aUF+j5wCpAHrHCJrkZBtBvUko7JSrlrk9ZWbVMYZp1YKA1ecZVGUsf0QmxEyMJQD+2l4Xc6gKf2EsmbrlGtCTBZSvF0xvQmlmOzR5asAmcZ0fTgeDv3yoFZjjszW+bprALF0oGs95XhoNWj1gJAuB6x3zzCySIW2bI73rkzybei56L9YPjw3/J3kDD1zFqNcvlR6+wbHBY3y8Lf26X5bxKqW+BrN+hAM/mswPWhOU7mNX6TCAxBftvI2I3Xvk3uCy7WcEUIw0wDPUbptZ8XfxFjlnf3vwOYDkPbDV9DjCe3UOrrbu4Tu9/90vuDA8NDg/Hvdn5PDsla+mA3dwOjmZEgidlIxXgWML4lB5ltafPCN6jm7Cj7yFxr8PLyHYKjKqcYQ3ZhEfHF3Sxjt8tbj+ufXUso737Z836khciXAMRHYRNJtnP4Zr8n92OUYt5G1QgDAx/c5jveyQeC12oh3PM7CLjl0M4nbFZhbSWvdsPMX+54oHmvn+aw6A9RF8W2T35emyGP0/Orcu58acrjp++HjfY93CuN3cqMdoDETySqu8+1DVVUVXBDbg5mQTV95SSaMQuMNGFelkPTcyH2KVPWe1RwefeVOBHnuqO+7cG4zXQdVq1xQPm3HPzT1q57fF0wHJBYSuHaNlUVzEZO8Xm6Ia280+1cVYC+LIYP3S10I5rmfa4zeKp4zdOeBqjeeqLRu++2W1od96DvZwhln7mnQKZijR7BtDCRJXLAjTTL1Yk/L4l5w+1TvxEGtNinWo0l1zIhOssOreTxsUPeETZwt/Zhg/Kd5/WQuLC/ZUcXrM89KihJoTCPIMQE/FvpJbHHK3C6QiThQVw+Ufx1n9zs3wUCNZ3qLUqbVShpYL5TEhxnohDmJmJSNxYppJvbXNLsvbaEPOL4NKAgxgPpY6mZ9ioqUnyPKFsB4V1XznTGjzeHEHVSui+FjgLIi4umfucoUNBIMQUxSRiZ8Nw6Ss/WG9AR4aoGcEZJzUoGNowgY0pBob9mTtr1D6n11AQBGx3175Cil7H5XHevOjEJpctQY/XbTYYdm75CkC0XGTqVNCJQxofc87AneGUKY/CRmAPKqrY5ukMINKeyNl0aRHU6UjyX4V+OMl8tE74DYQHe9tqOAWzl39tUxjRYxYDPa1ybsANyJTUpDh3ZfekoQd2ToIH+4GHb91VdbFuRacoMwJqJDiYhvBIKEQBHhI1A8W2lhQ3n84OJTy4fez3B1CHX/YNQ4HphhhiwS2Ay7PmHPLn3zjp/JzyRQDnlmEYauKQc+ppQ5l7urJgYXHXvdMfZyK6tsBvtog+/QWjfXYGYSL3S3ABduf2AP7/xHgSDQxBGERTHHa2iiiZgld6UTLr9IZhoxuNGLtqzzlE58sqpAG8EHUpNdU5nX/Hu1aGqnrtrGel5oVhoTQQuM0f9YOFb9fDw1F7owFYgTU6IpgblFFCV26GzwetPVS8SVx5GM1jFAZHLaGA9e6xl6wOLVZmXROwU68GiQ+ZyNZwxheozB82P2zeLNfqg+gMkUhaBoBcUxZUDS9ElC1WUlH4ilGwMaycfqN7ALLaYs2w3vaBI/tVO9yFt/56Mmd/vDQ51Xdo89peg8KHxx/s2LiU23z2t7AMNYp1v9eTH7YFdZYpyB3zXFPx1VXHpA9k+c0wJBnUJ9FE3m2AvpeWWiiWN0ewRMiTnI4Gkom7ulX1Jy3z/8zHpRLpwdHt9VVcJXnp1V8FLqB9bn85ESt2+zikcprXajP5F4ME53Rduov4O921UPrAd8mNwQs5tPdUYGLXt4YUWf4Xe5eD6GS8RpZwAa2MA7z78q5fMFKVNlMbS9s6qDIP5hXYLrjMvcjehAg5VBrr3M5xZKvDz6RG8Yk+F6Q5rfsczVMVlIb7J7gofD1xDLLuCb4n8XVSeKASjQqGuYYI7bYroEA14Vx5AyhMX+ytLwSCqehJoRCF7RKAPgnSbJz8p5Hcs8HGjQI6lGhulI3RG86zC4FnQDWhEmO+eB7KUb73s4OqC/DFTfu6uoTrIuwdTGMzlgKAVzTJesGOWRYVhpZsJpUZCAGQJ+H46gRpXA7gHIDdgIj2gXVdBlnQ8boAXKIJwhS83J+Neni00Zvzhzj+o8rIo70gBM8LHdn9v9i5WpVqS5srNkgwwXdI8trAZlB19UW2LL2XSWUBMZQ2kYRgNDg4XWI4InUAJyh93kfaDj4HGFJTc4q8a6PSc5qoOtVXoqosPn+Tc2Ya4NMTScTfJuMgloR6kow51vlufGEQ6Uh2h+JWYr1rrpdACDx3+TenFCZsQIcWl8J0t62iXo0JNl6uV9EsZ1KAKOOgteWhcakB7zjIT1Lhe4whD3XHITmrcKy/ntVwh4BtoPJTbFE+4dktqVgYVu6Z8ww4C2x37kDqd8TawFKIG4eQMecg8JToZZRNK5iHg41Qbx6dwbPdKtGWfjVEawpQ7lnMDYg4oCeu5UtwS8YZE+HK2bC8HSlLsjDZba34NrO99mqZFYaouSr9RrGbM+4jmMut3ikEvT1TplweK9Prd6ii0oZhUCvnuYUm9f+PRpQV6z+YBYNtxDPksdAGGcXr2gdioWsOsQF7qydupOLxb2kZ/IULylFxojaHg5MBG+h2i2VGldhzR3aYDdu+OC7Jcn098ybGxs3UF+wrMDbKSxBvxIBQgAzZ/odpJKmvN/MAHWZSvk4AQZ0y9ifB/4rFRYOGbJQYmHdr3E2faILHbUqvfU4kFvQRRmY2SN/5cFMM9j+CEfDpZOD+OJWKqI7jWQ6pr7J3+Rw8USPGUN8QLr7sgLG1g116WcSt+Uwq6LOljxc1ddBe1VOB+4kqoETpxPZVZUn6LfOU5ITR2XP4i1cGi/dOsyQndZOQzRs1n6K6fqlbWmu3AB87UufgUiW3me0jL8Nglul5snkvddmD9RNYEAfrjxsQw470DIdP63pntI3eIoRwrK6bDxwp+exDJXet3I62cqtMH7mJUzvq1L4+iVC62qA36okgKaf7cmZp+OmfufdqBLFdwNT9HGMgwDus7HXe0hK01HsyKx/hU6SeUnxeXN8AG5RdJTcgz+1ixWvNoV6/tT2NNA7YhJMcXR2SUX0o7AYKvwPXQ+2KmtxL82SztgvGtB4M3k7y148/3vcc+pyk2AMBBEufniWEnq3h3AlsiJfxrk3Iu0b4uKXAlT2UCOYvSZD2+C9+mivk05bJPgJoL+eXC/RaAKfS9WYp229D6YNqGzrI9JSUnz8fXW1cHyPNCmmdkCjmQuu9KGzheU5UcyrWxQxlqUt5waB+UYxub3Jk1Xx5YI2x8dkIQSZ2QmyJssTuIUn39AjaoM9YAPllhJPo/T20pIbQuuO+6XDa+2RUcYicSZu58z5MGi5lprUIrwdA7e6r2ViYn43OHITii69TlTy8lGSOQRTGGxDcq9H9lAXHEXekBj2yCyTkcAWiHO09lEELDzMwulC0n4pdiB0l56F7SGEtlD3eovLUn3OiCjhZGaVLWJn09vkXSmBKSOputXG5Xws1MBFq9fy9ojA+JbgZpmqglU8xjgbdnI735QNN7wMvqqrXlre3Bn2hUmgemYOD0TTR3V5kPPTW/vfnQ3zHplez5L6RsoVHaKWvY5nORN4GRlcqM5tFiJsBnwLXcNO3g2D7IQtUsQHM321df4RZjYJrlaRYiK1092KKl9B8gxpbMC0Kxkl48DDIBDk4KhkaVERWtqdTgAEhF7RGMbRI14nh+AcLS3VikyytwZudydGjZfhc3St4gTBnu3Aro88lijaihsUYEv8qB3T/jZCbiunuzvMiGANoAFX4Jz4J219Fhes8WtXau+2WIvzikTQNTx7J5ZRvqZOM5OH41XPZ41AaYtDv7UhfCh2MQMYDLg1XrNjJl1vW4N18HuCkAOPFJUJwg1ZpiKWc+dVcPhQ3NJwJZ0xh+jgO9/dwKv71kCsilJIiWkC1tRGHnzLWOooZ1p1LdnHhTdPCK7MmooCDezbOOIJOZ/n4l+58i4WhIxNSKEAVQxgzL3s5CKPsmeFtHcZqstWSSF3NBH5hTbER7+SrCOWTJNqg/ubMm2zYjctd8WW2kbvpPjFGaHLuIV6swjobCvHVXBU+tYEzr5QOleppSCMXp4mGBlSaNEqyKfa1g/luL4UWJrHe8IEvm5fRJmdR83CA7XMNzEfTf0uPMuilUaUCpTkIimVtceiOVKmNh6THcxRuiJgXYDkl+E3GAvXP0VNoj/SGyIrMDKu3VFl2rRXXlfR2jvS4wZjXBQ7u++xaEYPDnwv5G5ML+HI+0OjMQqKwh6v06ekhU6g2A7795h1y9bg1K9RNItme2gskjqdfvnTciAC4GM1mGsB2ZogMHRP5IzyCinL2ZwaETF6m6PtfG/4TqqgyreIIYKYCS5SdFWE4KAalTMlmUXBGVIy/SIuZ+VFRcexCcHGkfoCgABBObpejU/1tQ07Ek8rKaXHxVXgnr1m7uiGlBhgHKoVnwWHuIyz3RLkP9AJpLbPT8D95xBIiAjHS4fmP2VUgkoaAh3eiRoMBhTVRnZkve383vDcNE7aFe7gT43Jy6fC80+d1GjmaHntu+vKwccE6h7rU9FaZS74P++A/65KZtxQOEfku6K3ZOADLnZIQqzDgXnL8U1RPzCFCoQqmJLWAbFuEnlR1l311r32LFgtdSoAmd+Z/YM92i5e3SME9AUb3PmshHyE4p0cFIt4FQtTgsJoxYJMumlFjlVPAO0Xx2W3viL6u/koVft32dBxe6c4Uv3VJwV9mrMUihaPw9sSZT3nGjqinh2J6J5xPXIcdsuUTSklXxUXs9y2wP1KBOKCaFO+kskMnBH5jRpRPTXJCDNVOLw7hLONBtZyMlNlj7XHS68ZxLO4QVFRxk50qqDOZSgDO7JVupUJLza9PgdlyciEGVYCbX6dVLxPO7z756U5oe/7lnWP9o42Q3N2WBBsiI9tBbIzot2xKZcYlGxloR1gXIX9IWXhzsg4ZpU61mcTEjMTgNl/nJWwE0tz3Ug2BYkloSK5Kq/OUwyF96N/BnCbDm4FQwTNuoYTNmCcsHGO+VK3xF/U1vR9OdQBjtQz+rNGbgq4/1wQbvoOW2D3T5b15MvN8eSiStnBFBWEUtLgIGMD117pW8hcKcBmUjqfOkaJxxkOLUjvYVHlsEkt+Nb0jC5mEeTUFzf2x4ELwbD5hWsxpyGLocMz5EyYQeLlTaKLTupRs11wgdjinMzbS5dckYk4gxfyjYm/2hoCbfdyPlYs7FIXIGk1BC/mfMyi/y1uqPYTkQ3g4xuALdRnQepuxr/GKKo6na3VYMUGAhpY9C+EawLeIKmFrwXlPwA46U9gect2DpaVXQY23uXSLgeP1UHv9sz9VAe/ES5R2gZJnEKVwLdhx49bz6Dg79T5UsgPZ/tFyKJ65bkSM2VXDHqifpxEj6bI7euhHwgBSjUUrpYpBbl2taqaye6J0fp/hHLyrnk5xOllZdVL82FEI/cd8Jn9yR8MEC+qLvWCqsNU5g+TLiKpPgEHrTs9PCWAMkXjQ10k+T85CXMwY+AEX7M46Cf3H5Z2MfGyjsIGSdce9ldnbnnGy2UW/TBWzNx+t/sALiABkLRnsFbdHqUoHtZ8uEhG3Qdgl5xcMKo8RzuRHRsu2t2Pg6lUPwI/DECeWofuUJdSj+XoyLC3w/HIU186PKnJd1KuY/S2oAR8zafYZZEbvf2mRjczrJrUgNt2CuJPDl8KYQTdTM2jDvygc9XBBsqRIf9AP4zCoNL8VgdgtoE3OlYwxLlBU5W/ZaZ3OX0DG8jShtutkLxYdoFcF1I4v1DPWeLqtfOq9oY7jcoKOkTMloCtFBUqiQscZHf+iE7JNDZBmtOdKrVfy83qHxRTFnyFlBtB1lRF6Gq66U7eanL5b8OR/+8z4eijYLxDF23Cu3koUyO/DPSlLpjoVLQ7AxOuMNyL0+TK6wwMeBOtuRu7k3Zz+CaCMIegh+8T9FFU3OWG3KntXdz2kgSrsIT3/cKmoBYHdHjNn1Eic0kVjO11CqVYHQDwwWyWErns84pAaQJSPv5H+hm52zZH/9Nrox/BwzNrFSNObAbEHyL3jknvvLVrPUJJFCG9WPq0Zqm6+Uh2PgIJvSrmAX2I5ceNVMyK6yGpiJnPr21kEByvPC6LQsBAIaQJvCwsjutGXx0tIJOvdFW1rR/dBdh7qPTphLA10wsCCxpMQEDZlG3ZxXQQLvQ3QEZ658B7YhaJE4GIgwltvaPRO+3gi42xFhzIQoPBqe5ay2dWMICJ0DI01HUd+S8nIXW8W7Dl2kpwb9N+8bEiboZGTEJIhxIDT8wmqKFkUK/SwC5fiU3CKX+ZlzCigJXojdP6/ntEvKN8vIb8RophBt/+S2FPdOePLzkXYlzTQZYpAd5WfXR5igoGpuAZBT+ORBaBOVLs6JWfLe2d0c069jm+aH8z+TAZgnaI9RDdGGjSbBSUNY1m2NERvMWHoBFNsCO7K+pd9YiGbQEH42N35lQzFtR1oEJhR+XNtPE09skXfd2YaLSsZ5FUS91gxW/1oRcFQ5R3VObrC+HIs5JAiiwS1n0MIaUmGiddzewrmsSQkzT0pVg35O075gIBAj7X1CKWJHSTzGcvWGsobXaWug8fbiQWfjwZFH/r67b3DQc2ce1iJGnHpgtO8+opak2rq5XMmJNu8Od5ILPc0oTwZDsO9I0rjm6KAlYZENnMrJOcpFggO07mBpXY2GU3EaY+rBUAvl6nSUoa+LB69EnL1AKvJbZpbl2vxq3U0qeujN+wT61RoKUsgurLIfsPRRHwDnuS6n7qx626JMS797xuhZrHri71dmZi1L7FqMdZXMWWo0iUvwFlER1FKgfCzxCCSbZye43MnDpmQFmGue4r1urE3VTQ8u+GSS27GsGiSn5MSumrAuIbwUOE96gU+qvUWURnand6ZsL9U4WxtFYFKmkgI+UXvlcbBRLP8KzXo26pbiOYnO6iKmF+YAI7IX5Y6Xy+S3g40Hx23HhHnnrPh/7srg0v4Gd0K1BnAFYVTC1u0VM2H0OQecUVknf7ncMa9NHwsjB4AFR4C92KdtPSKqy1RaGbkM13t22s+34ikpuwwGLYSYATcNft5lPsxXgMhbKYPyFBEChm+JpYHyjZLk++pU7ZeiZKdxD6i76b54AcIu+9XTVPeaj9OcF2Ygmox5EG74Lefc6BbWgkzD6ECpSIkKez7FL/Z8AH9E5vZFplncDI8YdcM0kgQakWwJLZuzHcDgvrVnYdy9/531AaZZORGa8uyJWVI8xo3kCB0tatRsaAzLsGApoMz7HoH+RrKJp/FoIHSBfswN0A+nouD+FxZpZBsO8vPCB6k9AcPrRx1zwfdRpncpH43ew64+7iGYkpiCZPg1v7OKqYVJ68abu460UTNBhxG8UuNu8i0lYbdFMFGYOOvjsMaQ2vL1EOTV5iHzRKeBzpn1vJYSpBO04sd20dN4jVmaKjau+kcE0bT+GHleg5lnFCC4bl9/AujrsxIoQcbd24Tzx2PsDyn4fK1sWWrHmkyNe2JAU/n5gP9fD4E/jxUfYLtQr6vyxkxl/I7N6dD+X3Wvwp2mvQB0I8w/eXZR05Mq3UzEns4Zxaokhnjjt0iu1wIiuNEjaXDE3l/VsZ6EclLt0oqBsMNvyuL6BNrx5Xc1PzlitW4Z8eHsTSlcEhCoEHtlAbEQovv25kLKQARaGM3n4hP6DUlIs6O3D0ikpZQDCrJAB3QmWtEhUULhARbjA8SbfrUWg3A5ZXgKrSxSJ/ZRureZmONZHhVvZqfmv9MbxMTVIg5zb89CDJfXR9q8YOM4F8M7JmWDvP5sskgI8Qr3MbVTJ6WsXF1PpPlgiWn3Oc/RtNzTU0JlDlEN6BAM66xme0rmoJzgAlf/bogoeLqC6gKJg4AGAuThiVrbaE3Of/cbPnViH04p0h5OQdp7yYVNgw/hGwhPBzkiaarYgHlvhKW8Mi8PA5X6oQFmJBQ9ssKptn9mzX+rFGQHdeB2y9Fj295voQK2PdGOw4oXGwoiYtQO50p/Puru/97It3YSZDEtGPPVKE/orVZ+UuCJ6obotSSRBjZ4iHDY7XOS82TnThbEE5FXbpA70DMQ1hP7DAvYSYuz9xukoPMheefcBWbz+yHkYsBm1llI2UHYgXns1Z7l/uNuZ/Mzz5v6yn1G8QaJBSlYApP3fYkXMxs+RkIbYFmhZslNZ8N2UgToJ7TvONJvL51v5dbg+RW3ICw0EKgkFOV61Hp4Cf8JougvkArKUErwfk5I76Clbfr/WENZwXeq7MltGlPhkN8Iki/DiZHm1AT2c3wcP+cup0sLQ0SLpJyvu5VrmRdV6oXkBNqtILsvo7SoWnhwXdqTFwcwMFAo828cu3S/T8maGJVVj/05Z1Vx3coANnbFFxJ8DLLpKF5aLujqB93J00sN2ihlzCyU4VBidP+21YZLmqGQsqNnax8VlGy0iIXN7yJspwipJPJraIwj/KMVv+XiYXfLLVCmY6sEDnbEke6fD1hQu80o6jgQGmEjIjaEfOjg9/oQPYq3sJ7tw8soAHC6dUQ5meOtRX5ya/FYcQhG3+Cbpr0bRnnH2nZ3HYPrL7OdcqWASomhXx9Vii1/puKKcnW0f57BNH9JX3ho1zV7N7JAPFGy8LWEyexUbZK6HZ0Qp7gaoM/dSj1miILA9ue5EmKqYwxXrF2LurSkn/ebPViCSg7gNXPAIITGBXUXo54FrOjo4NnDFLHfWTeUzYkwr1YlqopdA1CSsTqvZuzLSiFoUM58nW1oS7yfEKn1zM6v2Z6ao5RGTTbuMtPeveYmlI9Uoi5oxr++Lgvs2pIgB/OGVf7sNVhLVXG7a4ch4g/GY/5nZW/M+Ndyd8ICLYuj9yM57HdbOG1ypMoCfk8Z/5sLbqHYo+myVPQMPUs9Ktjy6wWwCPM2/Xzxmo5UZaE/sNbT4/uuU5V7hgaYVeSPipzjflrlZ+4g+nxF3RUWaSOO1wysIf8Pk6xcLPotxKgJ3mM/ZIc7A1pcf0l2P5EnwTvDMSBzvyWV9hpex295E12hBF8usao/yFWUGX1h0g3G6pQCWkcmvoSSRD64YNVuvt5wC4PCNW3MVyspfs30KFsNxcw4DfP2S/CJSRlsC9et2LSQHqEM1kn9op3rkCxxczhWB0c+Nud10Xh4L/9AHiOXLQ8us8Cb4M1jjzXPNqZBO40PhsEVZ/C4Ss1h4OTJwIGR+bsUhQ+nfWaN2P3+jcncxDpTi3IGJM3DD9qhooOchVq6c03bcJAWiZK4LDENEZIOBQCIBXUwHfLn1HogHUBw5Tuk8uDxPE6J3hI3eNVAcW8uJJPmQmJP4aiUVDCBYugn2IaQovYNGsdkEkO8rqAqL1XuzF01d4APJ77NU7B1nTcYrw+MLzZOY8zSScKPbqW1ufAjTGTrCLXX6hcPQGJ5Nnv52YXBfJPevE60K0DrOJGPJvWUosjzr2G1Vj4H6pThdQcjjZ9E61YJms909iOhkgjiqV3oelhT0jI42j6aI2wX/DhTIKZanYAkMz9IFVmE7qefuPVnz32Pl6nrdZITa7ML4hJgM7WxNhmJ8vPV6qhsLmOL7LHg+hgVsSRAx26C7jOftO+FLQCDkcNcmS12FdwesCIDJZywkacqJM9b/niNTQjCWmLkHjsztGvVGJq+osvcVHFCXykMItutrSgmFQBQAMrYT2TkMvA/meyX0e78swJaI5ck7JkXOlhR1sbMFi9zzdrFFOmufV5vHKRdsBpf/PgY5cLEqLxAB+BOSxEHd7AuWODeQaThSfvg+MN1hbtPQMShQ/ae9kRm9Eb7iqfmNrcT+VCIGaWWKSfVQYyay+LDy+ZuxEfHDbsR/pXkhdygWTN9qHnlyBzh0dtV3+0C20eTvOCQQOrvm5KCIeRXmL+6feqrvpT//g70h/BjJjx99jTDz9jJjgGM75IPTAbHAKfdGaeBFQ/8RwJwj6vQ9fioxfeSj9CAyXHwLui+qezHxVNtBtd1oyTpXGTcbBUa7i+SS6K17uWcRpIeoSTWkEdvLDe1mQ8SosJk0DtkIBdAh1TiprSP+FThBWqFFNn8n2rNjByYQwHmnDCybY4ZBdHz/AcZeh5SU805XcC3UuA7yOE3o9VPY6bT1cuclFfEXJobyLFEhGv4NRCkcYlCowbPMUxzH/tHLwAvWssz8/vWKcOVGq+lpsfX1kKvRXvhKgehOcLzwhXKKtTCEYwQacBmxKKEqyiPeCKkq9y3b0fR/c6jvo7HZIbbVvSv1QXa2229y8y7ExIfamyXBumMt5FAiFL80KnbevOjQ+6mcAsoivMd/oEAeiYeYHwsuzRwiEIh+++aJWA1ehE1k7tntRn2i98wWyYhsJShLWZCx4dtOOKlQN2H/zTBqW/H0d8JxVJAavIQhgXLks+xxAdzYNaKDCCk7WYbYPs/1iEtcDXWBTc4q09TqhEWQ2z3afGTaRPAgeWiiqNL4s1F1txGiD31WKZiISGQI3lc6WcgekjshXF349HaPLFPTUFciYkArMo0hgyRlvP3SuGyfF45YENWstXhHv0YZZ+2yHGAgjFKYtSKx38w5akC2QF1Fjy7hbiP8OX17dU/+ZjbfL326pF27KnNZ0tDlvQdTbrSLta7eJ9P8alncXlsbXr3NUE+/XJ5bcpuWrC29GI/GGK97qnKt7sBemrXvGbc+74COsR9fry/3XqQFdiqJB6MYBNoERvXOtnezmbhJDy8yK+lluQa2H42qnljGZvqsdCfHQ0K4lYorzHxpqq2svwtSgTpxisR1fxNCNBX6ar4AeAdOeSSGMqT2RlbC6AiFyD6aspNXM1wkEml+JdiwI+rchYoIn1QDF4i11OFqr9/hDFKBXIRc/6Lvbx7kTJ1FNSrDMyPAfo1qzpetLtUi8JRhNf18cJFkBrdSxFChuW09dabHZnbd/BbdxWxC+HzI/pUX2zhkZNYqj+aBK+UMyxYfHiJudmKCdNQKU0I36LRhXxxEl2lajZeb8Cfq+eHu9D8qYiSb/D9+F1qwCjkc+S7JHbIGrx4hIgBM2hnPfX2iVAwPwNLOlVvzYM+TG46eOeUyEAYkP4KRXa7bmL41QOfZ+OS4pnF1hxR68+RiTk2miEaxEZaYvVfKGJlLpA910R8K49VYxnJ6rJpZOUG82u1wBCf/TU8/UmuP0WFEM8Jz43AwL8Cs1R78CZH0sEvWKVob3T6hJzaw/DmPbPLrZeAnVvaD8UcbnOCJznLzcfXAa2lv627K7qbHDZEnzn8N2y3qFfd+4/eZHJ6bjYxCUSXz0ZnpnZhx+/un9bA5zX/xeCd1I32ejh8yk11EKI2tWCpMuhUItsK4WEMI3NY7DwS6ptXcDAWoVr8G28nnDe6pC9N7ujUDPxzDov37th+waOQlkVEsAgBvvYDUAKAb5+jK6faqalnZsMKr39KOvVpNqYsFpaIyVewKaeDLYTYo1DuXDYF3ou9qC3gADtw4QRDBDrOi25GsqlWzA2w4/YfYMd7YFp8/xIM1+sizWj3wW2Wa0sP0XYhnuOO8SNqzcoU4JUrqsK8dEgxe3pTry5C1u3EvwKivDgC8pf05KxZmF0R5IhaC8bIEHdOTZquEXC3p8ndVN4NB2wPdc3ys0enVEc2QV3FVMu0ujTtq1pW3TWlOWxmcJLM7E0Rm3zEHxS5ipWhWyAM90F0gipefVZARiDIV4bn9q+f9WTzjkQ1okZcL3NDmVTKfe+EQ5R53vP5oTOEj9WVIvDQOV+NE62WclccJbIFbThZNqqzHM5bVx49cH/php5rMmunoUewc8D3kVMlwyWXeHek/tvlbfBXE2HRoqw66NDvn38VAFhA6SCaqHJakSnLu72NYAY3m8NIvCtsOl6xw+xWv2pc3MSZet1+CbPj6EwXoBfHVvuitNV6WEO6LoKA2/0rtb9xO/0/n77i/XFXm5mA0YCwfJlMKAsfj2Ts/LGvcUolPEYZkO6abymc5aJFk9IEpr/vH8AKHMZEZZhp61c4E0tzAbYeVF87RHbPaWaf8olggCYYuW1PBMtJOkKwnRS09dw6dOvUY70hsxywWrVo18azJ3STtyw9Z9bh3wynynokxfULEBjUZeJBcZVxs/50hOZJeqvSj2sqReeFcvxGL/dYFQdiKCRZnJowjThZr8vbvbhX7FBNy7DMDtmFGOggAOvfHYLORS2jU2dYfAnecWv7xumbaQ1twhmBHiIhUxpQo8vT2U4MxqisC6eyR9krEqFVMvABSYnAwtFO1YBYgjYU9jF48KSZtUOueNfCy/RCumC6keuJ2xDuj+V9rzBPb8Au7ThffQx/mjf4TKL9FaTT1YoRLDJbQ2hJAPBgu1Q3MkrzVyVbxEPdF+8yHDPu9owOdccsJsg/0UPwfh2lTUbNUt0WN3BffljvcXw4+rbRpO69HPRmNwV+GpzQTR0VKrcWx/DLtbWPIBq2U5g1VNn1JhBSMUOF7Y9xzcfaiseLTBEBddgQa5DqXIskm/WZMbO3VtqkKYNet5oGS8nqLgJhQEIVypW1rfXSWwwngkVosh6vSVvo/TFBgM/rlXd16QDfPf7ytdny+ZMRKdRk724oIiEmmdFhzrui2YOiclU5ltmM+uAMP+4AvKIfpSaB6RSQJecDk8quzLRnE6ZkStZVNn6++788xQ/BqifK4lw0xvZaGXyTGf0k4B7fGGZC5BNwUNy6E+JnwdLyBfAY2oJMFKJwZqHO8YKu71A1HnlXvqFXPmriNlAB+OkVYDRRwT986qseN6oNkLF2kibyDfqWDBvdfifmrUUOfsaNlIilIO1ajzenVJHkHIScQEIQL9X8NVwyY+dRfYyerBYrMBWORLsvg+i3nX3NQYOkamXLcbiL/BIvXzVu0CIW6WzUr9yofEDtOT2JFN4DMAJrTruP8fNwbnJ2eqeEcSicv6YTHmn0ZmR45IXhh8yOHCc+I2jKUkdEdI70FDKrvNiut7t3mbTVPe6xzlrAOhgcSE5AqrrqP8mCOD3l4GT2sqqboFwKGoa8TxigYPwse/FJyq76B9JXtAs7rF35GZTfPR5cN1W5MY4BL2Dy9Z6atqSyoDpI7txWxjwmwHq1mYmVpgctdYG/+GSwW2Sa81DJx/Ix39Hvn//A0gTMY/sODmir5COBtxfVu6Ie3ao5zFqLmibnPTlkhXD8V9tWeuTaD5MeRJWPfTjDGZhBbpfdj/r9/0dUkdjJsS6sREgduMEYk98eVjAnIVkearX5OuebwMjJUdRB6SJPsQrfTIavZIi0kGAlXPvGE0vodt/nEpnwYalK5de6mxVkXXeKTmfrsPFlp6x1Pk1CGBmrHOX91pFuuICg41FCj2JwnKE+ZpVXTEgcJd1fleCQlJqByt3boH06eZMYYgNS5Lx8m6p5Nlz3uYjljsYxSfTI5ax9STnurcfOWXi9fWv6HC3B+iOZYjlhORIUFGpu4smQnCpW8kwZInDOAArMLXTJBKxnkgnHyRPQOfvhVy3ehZrG0tdiz5amO+Zphk9Ude0ZfiES1JDkDo3+3TsS2jQC1ARXX+ZmOFYn7GsJnnP0SbGKoBeSGc5Tx4ctwP8uyaniUOcRv52aBBDYjp8NkFkJ8JpTnAbWEim3BTY6BXaXs70XmCbbAzr7o+eOmjy5QTVTNsNRgdWJtckCAkMHznyWhRWQ6Bavjdlox/QtOQ1Q9AoYu4ueEznVweR8HGCH8crhG4sFmJxuv3N0OnayBgEpIMr7S5aNT97m3Uf64f2jhJv2sIMu2rYMkhWhFyhKtzRXJbgp3NDx8LsiMCvdDxHoSl9EUfgC0YZIsDTuedqeXFpRr2m1FKrMXC2CXZyD7298B7DBkSLk9ZH8ukWlw5KJ8bGfO8FEqv1oEp5mPJQjDxIA7peyqpCZ5YgdH68wMcteIYcAregemwerD+O/nFo1F+RSg3YpfIQnuXvbhT015ih8ilQ5Im8kugyOyt2F93qJpvh/j0u0Br15HngJy6w0PDX+/Wfvhio02ZfELfvV/sS3yUamC//WdKAUVYgTV5TTuBEPrOd6yFd0puEGW1P1vbnhnC0+TKnAvwTxBjOwtnKVPJSCDfUn1gIT+lkaSTQigpVV3WJi6orD78iTxxMfmIG4LXZtbUC6cnrKqEKrg+eYGaj8yqKglhgY7iah+4miJagqnRBp5YOYaRbVBllqZMvfiScs0hO17m+mwwQaVDYqOWvjKF6aHwqIEPgoKFtGjIi3sLkQmJCwYhDMFgJEFYAXHGVQGNIpMXHoSUS3UEBJGhgKhKQN8jBSBwE5vFJmZwMzYaYVVEQI0FTJSTySPrayGgLu8gs7NfOomYYFHGS8ziWFSwx4dI2i4Mb7zl98bbIH+OjepZ7lSlRYH8mbPmCDNRAxsNjF/mOUo7s9E0wfQjCQ/IgHzpPKDNUQuoAH1hwZ61uVMIJOBfLdVFPE/g3z1yp10SEKxeRqYgNVs7Ya0bzERHbAvrq1r+GPCgStqDKe892IIhdZKHgERV5sqAJi2jsniRLVUc61R1fRfW77pVW8tLWfDhLqr0qoFYr2I7ugdkepPP2btToqFzzmwWt/9MqqQ8DozG6vZAA3vYIP9evjgFS/fqN2KNwNM2XTta2PrpQmEYhaZa89q3naCKkQfJOYRjUFCNzYUPdGOhbip8MobpB+JGeDYM0qAZcXUGJEdRbD3yOIItamQXXZC1LqJxxtsdgQdjBWLtwfNxrT+R6ns4oRDA7VWtHVZiXPe+SFEZgJJ8NeorQPt3a//2Mj/hDb2iba1ti2gtWiZ3RMKsEyIPIfCKkoS9UsZ47PuJoq8Gt9WkYqNDgvfFugH2G30IL0dcNFFO2xXLO4nETztzl8XhZTST1r3vgzhhg6q7IU5WG7Rzmcx+Ockz7uQx67tl75fzHcA4eHSp7ukLkzHIgJBQv7HV2SmV3b/oLM8RPzMhOr9w0SD2Sms3nABBcxDoiBbAbmHeln6PxNz25IFOlfbarQb7076qXHugfgZSJSJtdtjTcozdQ35Qfl/XQ/vtR3yvKEjfre0b7nawVOze5qRq2Ntryuf+j2nOTC63RuB89x6Cjz9xhzZ3FAvV456EE32WKShD/EyssqhWqlEKgTaMR/yhqp86DE3+GUT8riCjl0BIYv+n4ikK1epd5zFUhcPmj3HhikneLlQEDcDkv1D5XU3fX7fWqWcO90JD4a+Qr/1etFDOSicjECaHQ0IaVlrY4nx9s9X4j1fDOBk0SzORR1c1i6/YgGJiOgMtxPEYPyCIzEe1zo4Y2RN+NSRntpGNqrYzb0JSu8JYvuxiN2c+L9nP+4sYb7s9PvvEgDSi7+eaA3cjCZu2hBt6//vNaLK6EPZP1u3dqLjL0Q2iEolfUpMgVAhVsh0Jp+iCs4+z5Pyw+Kt36ABkv0qiTs1VzxUwOSlWhxKplohANu1fPIemS1BYeO++1CkN2xbpYm+bGi/6roEpJy3i7IUDDcCwqqRbOHTQ/cmuzQio48hzzCbpXboFog5A+Oy1tZQvKA5r//USUTAU08vmy3Z9Z1C2CdT/nA+SFytm8HEP/49DDc7b2Jr7GB7pewinT02YD/XTZ4zpfHSbzDRfcIFtX5cxSXqnBzt1SMlqy74yQg5cGvzMbBnaD9KJdayLHrEOZ4bhbZQXrbMYh29ketXREu3/B2yAWGIcI8zfF4OH9R8X3T7DGMHK8zjJbbso7qPlghBK+Eg4zRP1oPLk5blooDzX/FNV9jSoDZ91niqf4e49wG7WKTeQ+w04vgScrJvKib5Hs2u1sLLSpgyrmNzOL+AnQ5k8XNMtO8zK5QOODEPhjBIOiA2wEyP4iUbczGgkMbkBaUHDnvGUlwUAytjl3dLFLj7X72aIknkwZalF9/xBHPa2JrepWtPAtY0EH9OYhsvBbXden6hiESv8aGASNIklNY69RbhF87MpT+RoyPC0hLf6q+HrgO9GOy5PTDeezN+XITR+Q56H+QrgPq0mbNffP9YqtXfU0sSqRCCfmEeBJhzcLntzV7oeBi8Z9mBqQrnQ9KaIejBWX/0XH5rKyO/CLh2frlWlWMBvBgX7lHpJk9vFR1yPEJuTyID8qNGCHLRgh5ZgspnAijpty6J04LGne1zhDoXGhi+SZAMlKQz8UiS5VaauWoI7HjQwDqC0RLbTuHv2uirqV2EhMWi5yg/E9sVtrQHGLQy5U5ZIutas5+Rs0+z2ZdXlZqIWn4lU0m71C/BvgGFxTWELMmo+XpjvLpf1mt5f6SeZMeT2YfHMWhDZwMUT3QSsiWJHXIZkOdMxI7qHASXt97aPx9CY9o7NJAeokFMXOGkqn1ozUTNBuxz0xmOqW5p6Vt8t7Stw6Jib3SrFXqVr/cwFM9819Mwkx8vlP8979ezm6fhF58RPiLablNFN8HX6FYkdFfZ0odSD9DO7nHVtKfHU4y+NVLWFVuNjAUYLvwEa8SinMxbCsreJBH2R2W4PA/AZldjVW+5M1nltLIo31g6BwpIeMN3rkYkHTkMZs81HN4W6yIfh4JYsqyjbeSrkd9XaI1Fkeemt2f0cbHKpCQIJdi2WULy5ruoUYpK5vSLqxmsao6BkQIBwfba8uiA6gh7UL6p6beqxecLVCLm9AmlE8JQK8L9X/n93VpufJqkGwIRgz8szS/doPutCWxQTuUvehuLioEoZHbhIupGt80QbpJlViAIpT53wPlqkvWDMvG+jHY7Yqiax/mB+tF37WHRJXsUjVXc/lsOYtbPPxD/0Vswj3ndm7y8oguMFLaISCm54KnmNOxVGeHpb6Ah+7okuXSQcUTgj0bopHLdqeevkv91tAaxi43SRWzs+9V2R4Xn4aUcrOUMpDbzAA5uqMrlT0SWqdnbhMHLmIYuwa525bwdEQ3oba+Y+79pMia4hBynOtrdgqt0lj8VvIEaSZ+jjvieGEbJYE8gx4TbuDqRlnwIepu1hTDJ4KkA2tDhScmhY2stohsHoegQx2kR8g71UDK27sNVUDnoR8ZFHaWvHo52eBr3UVNk3TcR0rxjxItC+7hip5lzvQfS6T4rjgJzacNpRLYuqt1Or6zicdoMFcm43PkW+EN6x7ePOdXj0G10V/Ph0mQz4DsM3WdCNrNFOuRVpM2vI8D+ySpogVMl/KnIPudMLlcB/e6o5mdbDBplks6fvjQ45feb1ihNociW/LHyUtQdj3TTAb2gGVe6L9G3hJ82D5LG7QKO3cJv9OtMGEpnukSXH7D4jj33n6k2hZX4HNOBfR2HaIiZ2iSihwTO861sTQ5xOZdpdy7hkweYJYTwOPCK9qlkKEdxl++LqaR6YYBrxi0MLChCUas/jxtcqEGQcC203C8ywvsu3gdZ5gc/Tk6W7L4wqjaru/Ev+YtREEmOI9HlO3TTELC0o4CKWe8EZc/kqDmfqe3I1JlkmWgZxN/MCCxD9TXj7hxSgh2cfIXcvj6vR5SBhvHrITH3V0B5ItAudNDxZPEAl7W3VV6IHYpQkualU2lzH8zmqTxQaKKzo0vNQb4PnYYMbYOu37BEhMVfCl+n7qROd4iZEXqrt58ycHxN0WIUo2TVEDUEzPfXuTEsTw0jsa8Hy1hQZGW1TvJUp7PzZ6qhvQLsxZ7dvuNoYbXGZDknfeF0QS6yd5x3omqDr30iuH7J4lFq0tv+uGgQFm9l0ZlBtmTw/pJzVZ9mGgCei0rze1n1tjnOQkc2C//CITKDfTd9gd6YvILc58hMaP03MNzoKypS0sL2RAjy/ackrRxQ1eMbgxnnpueuuHaUam+/TQ27VW9HKkiMOTwt76HHLa+aN8fCPjfegCR3E2KITNeNE+G7UTRvnaOXJan1b2srix0ZCRGdDzPg5dqiJpGf6Vhf7rMKhdJCos6LkJ/XDVzDQ/RF0wcOtYeeoonotXeiYQ13GbX0cHJkNQGcOA47bA/7/P9A7yS0yfIyY4Bh79s5EnxGuz0z9v0noJCqhr28BTkjf0LwHJyJCyKUD6OoI9Cs3zvDMT8e6FFe7M7vQAENqtAD/nqIUwI1SGjFnhJ/lRC6lSDstXsuok0vzrcN3e5qs4u8IEUZPObS/QjN37HSxGuiupGKAfNsXoZF6HQtFLMaOc/nwX/VfqC3OV3fCblnZc1+nIhJG7cqgr25boLSEms6vrH5byZfC3l/smuxAl1nb5036/jPwCpVosEAeaoVwJ32fNsVEUx9rL7sYrikfNy44zjgkWRF3uNlrtK2H5OQ7yZrCbfnCaR4fKi7prcUVRW3SfiWBngR97WMdvenucCI3zHooEI19BojDhPxPOf+LVs3VMPpQj/XSCAyOaxM2lMEwPjMdBefRc2+/twYuBLcGyjVOh8IBv9O2bgYog+sGLfaAD6tAR5+3KGlXWQ7qn2Jam5zPfokMmBLCbDO7Zz1ZDWUvvwEyFNaQTM00EXMVHBZo+XokKdO5TbTmUBhvesodk/yrGajiu1L0F6SN+WPv32xUtOVvBb582pNQ6qQZNly2QWvY0Y0PiZU8boNEDcVXey3ZNE9AaYEy8jQ4mWA5qAfXVJt4tMIKQ6EHxCSpt66jIWgQ1sKsFYgVJS3LQwZjbdN9WnB0pzTPG/J14EeU8sb5jj7YmouM9OpbXuzQgtbmoY6o866dM4DJLL/AWYa7qa1MXT71z4lo47eeGbf3XjSalntTQANkSMaKf/widrn/ZpOi+QLdkK9PaRU+Y/WLRq8cdJpFK38NwhGzK+AKVtJhQUO9bagNbaLw0mP/l9NPT6/HmKVkJpGjorB5UD7qmiRh566YFBy2X6nOg+15LCCMk0UqE6C0KbPcmf/sVvKHBzufthYYxtQ+Dn0wMFX9S9WnlVjEsQBUbM5MljgbtebjODy2OqlqOw8ipPrPu36yLZsOGyRCRKC3F6TvZ08d2FoIp2zW7Bh/dwwecrtBVzDFsI+YThYSJCMGW57QH9TwRAQR/4lxJo7VW/tAfMuJ9OcrFLbd9PjfWZlCmJ0lHcmq79lZ6+ecubK5IOpWrLA4GEMWRJwYl0jacIlqh4gwT6gmtgRPiCK6r3UWKorE2c42DeoC1kK0Q0ycakFuBguUPmT9SnrQDe2R+Z7QcGjqL5d8OfyxforCt6zjeQavVwVhApvTjgfEvKVUtC0QPM8riKfdu4U5f2Tid4BqPnCbm7XZNPRXFjXYkxfs9RPHoUGnf0Vos0unARgz6jeGliomNywVP6G0EUXMZIGtDNpT4LJmNjSDt5Nu1DGUDH9mN7NLReG1mbsY1mk9RQlmWCxobj736DhBfIVnHuaQsNYgo3N5RByttKvkTyrr0v2PnEyCV3NxU4u9WfI400zJTpwL/fmeOJLJOfgKdIjK6NU/DrlamaFOhw/a+HO8SLyXJ/KTZ4mIG0svvQdRLZ/vzYuO1Y0apDMwr0QrSOAdATUj2A2jtmnAZgDl9I1CuJca3Ds9H1isOAO50GaS4LIysEASAukJ/MzpGSVo8XVv4MpeOiuvE7mVUiLRA48Gviw2Thh5VhSIEa4JDyYQBVhInACYNh4WP98iIhvVODZ/6MTkTDdtgTGR1jwO8G+FrhG8Pu3G9r0sRAIo74M1fJL0UFkdzr3YzcBerJwt1pxcsZ6+ctsgzbofPb2klrtqTVRg22CWMvkU19bvwvmeqfRV1sVU7YuZ3zjmxE0oimyA2K6dBbWsxa7WcTujgiYP0vkeCy25vY7xs56/vyTepTZKWLDblMDEbeNI8Mk8N+Qs2kCWbkU4KBX6gJ6HncX4H4qHJBNtYuA+acvxC+e54eDoQvfHTBP6n5Pqx1jYkTB9SYK38fPs3EZQ34P/xIIue9SudpaexabKHjYPJvf34PVtbk65LslEZTfJIgjZUrQyK8MZ1VV4vPWK7XKZ/NqllIvpUI5OPLbx9uP6mbQTmZ1XBp8EmTQeCUYKfNS9cysj11OZYMztbx41Ug93Ia6V590CLIpUOA0V3wfSTQTxx6U0IxP9pPtWrNPMzBfW7yfTax+hgEeFvML9q6HtUjxCIHng6DRZwS/qQ0ZCQJtGA0ikYOny3gkEf9R82zLe/Ez/Hh3zUMIqqfec7A2iRS75afVqm7rx/oy8Ff/lzrUqWVaZwsFFWkqny9WroPY3aXmq562t599/uU3hHARf+iGRlH9Unc2H8MziJRHuUArEenhXa0ALQ2PAP3sNtJF0kxWxsoVCXS/aBtjvkN9/vyJVNbBSAEExzd3w4aBx+Z0thq81+YKuXly/zMhlkRsD/YZ2fyyhfmbUWi2awDEnofSVPy+uiDRI5e3BKzCQlZZoEJNi7s39vpqveBuD4oQxJMRvvDgNjhUMTvn/sqk+3le5RF1TxgNcVV/l+yveN0sHSzNFzT+0BppkC7xL+gCf1c9wf3U9kkkY0SCRMiOllo1NZ7xO5j8eP2SI+m8VrNZATN0YbXzTIcVxKV2QTKHBZXF54T4e6pjNUMr9zUkfiIBVgDF7Fswy7faUISuBcptnSow0wLU3qRS6UQmezX/gpyhzBQI+F59asjgvlcFWI4I7+CtBcrXmSVoXuIFdbNi16WtqaUOgGBIgeSuy+81KsfGcQt4wtmABxgqAxJBGxEIBXz7WD878ZXVK3E5TVgavfXUYkoTxks1Laj6uNm3QX25bfAvum0VkRlBDUQUicCtlMRc5/r6yaIimMXIQ7BTAMaFe3qYms3vnc5U2qz9i129MDmy6EgJXN8SMVXL6t8OtVNoNYEgtuciWS4vszY7a2wJnlManduxVZqzNEwGt24xNqaRACfJgKQX34pcMHGn6DjXRcDYAeEzqhCO70TZqeLZPHu4ZyylifW3MMRkKBdNBKZrFuOMnA61aFN6VQlFiEUxFozdUyEV/NrXv6Pek2TZxQqFjErMrLqqDVhM8qjeoH6a/uL/g0boUqlqMXhuRX5/zQs8nEiBknNgqD3gPbTX9+1MeTdYsiewiZWszqVQ/uZgPv2diC+snmex9/gnwBXObG7t9FyQhJb61AB3RglwdSVs1MV+IE91IGmaWbWBD7eafvt+1M0lL4YgWCDQrhiXiZwuJn26u2LzrJXgft3GWpPXzHBzIKHAMhZpaL4/wDBy3lP2l43TT37huRXh3OErYJ6pJrrw67ehIp8DP87BPkzhptRaStaW+7y2aDMZgnozIBheQfVEB8KdGOqicKszdk5DJ/XnLu13R5QjkUDNAJ/GX1wLfdNBxV3hWD0pXSwVSQXI/DPUjH+2na+ohyzKNqFd2WJqtd6Z+UsEHHOCuv6RaZsIdPufMRuYQEI2QIXttJIb0vaTJoVHpAl9UeMefNTNHQH1fpzQOBgkCF8R6/uNANa8RviYRMY4OP0mpuftNEGchPE+uTousPOGUg4+xrZh2gRpyeTno9VlUAenUBtT6nJTvf7RNZ0w8x5aovzHOHOzfDfua8QsrFxvjsqezdd8d3Dyf5Gh45rUsJ5Wfp/ZmYgzjv0Tb6yahXtBQcwYoQBZbJtKpN2rVc7gzlnvMNQfoluMLRae+ANYN6qtg0JiajlUH60olaVmoP9tPazAT2e6ciRJ30Mmhk3VlRsF1HSoBwRu9Hjo64ieFbbSZjzy88tZecYZDGCEBCC8Wvz2Uxi7bZmcOVL48Opvgxm5HFAEYxllbsGvuy17PXyU+y6MBxtGuvPayA6KhP5qHa6WUu9tae2uRQFlBx/eNjQsxbaM8Xw+au7D9dKUR2xIdvYeB3hTSf01qMRygaFFvdhUyrM/aWAd3vSuIEdE6Df1K7NUo2zIj4p0s+/Sr352slciFjIMNMyniUAT85316s55qh8LuIj2kSh1ZxrvqBEtPlqUjMINtIftCF9Tha1swGC1BzG4yDl1RTpRv8/OT2+PuaAv/MaqguRmiJeuOMNDj8/Dh01Ivgm4w659g3gCJbFqkOUU1HoT6Hnh36eJj+9YFfuD53zj04rnFiNQrZINCeQfxmWDzXwKmnQHbvp0fs18eBhtF9FV90HPhPpCVAVDPr0bfNel6nvTJBm7vfcEGSUhlwZpqdLeqy3e5XqdU6Ub1g1NcijyW2Q8wOVrmWsQGn/bnKcKsiLNy2xxW09RGIlpLBtgcwQP/eKhF6A/DtSaSatKd/V9y8XEhHd44Hr2z2NKqOZzu84cV1h8Hw7aIwwiVNf7k5mLca9PgcrLb7o0RiWQ3R1cYE2WDyjpNLU0JzsRF/yR75+Rah7z76bBvRDIHtFm8UZtreyPxSeVIN6ZpRA3zlpx2JB2aoVTOrRC/9kiiErFZM5pX+xf+peUsVyjvHpbVkrJTtgceP88xZSTiv40jGu/duom5b8EeyGu6T/0YYg7a/rlqjS72R9F3zoFfLNEdlcDaNVnDVYMDVwc/xS+taJ8G3gwhWsinJezrU4U+vt2P+AMBmWY1BFNRqKf1fpr0nosFo+EVwnmMFWfAtRNdhtyCg8u1Cd9tXQHiFeIbjiVwFu6XrogXHspwnDCwoASrD3ydGBOOK3z/fB/SNFUedS0eEn8MF5vHNUyWPN0PgFuq29VLdB80PXcgMEk4hK6EALRMLr3z4OkCyRjiXAU5Fc73UfSaT9SHZXR4x/eI2ewKsIaa8Z9ESH+rbtJ3Mb9YTSvX7HZ1Se0R2JS7b5KMM4pRsNpEtM/PekWH8q14AQAgGwRWCc1I+ZbfsFp3JpeR6NdTYHWXJvCH44VDzpsTtP1iEfaYJaHiwMAHQ946KnyBiLc+3MdnQxAuskkk+K/AKIKKfP/ltooC8r+2tm84yzp7LJsV6zv7WwG98yZLshw02Y+RQFUio9UnuZlNVy7iF1Wmh1kgNIX/Ceyllq+JEmksHUi5EGARBRy/Oku4gW9u8hRgobDga0g2U+/pUm484OYufbRf07WVFkjQYhP2C62i3XM484+uA38E0o92U7SjM82Nkfe4UNKkBGEFHkH2yirjA7VXfzm1TXyPwM93p1nBa73OF9Lmd5mYSzHS5aPXVVqTZKk3AuVK31SFpnWdlIXWIPJE3NUlf9zDUm6elxuZjkTRZBIsSfFYHqic9V1086l5MP3Gx277qLZUulNqWevHzYgrdbnwM9E/2Zr4uTFkeB8bzxKjj9JQE5Hfk4RxKZnVUJYjEjXWIptFVgbcUdwPvD+jfc5oWerIZ3RJso7z/MOC6LVqNYfhDMxt87U9lG18Ibx1op4Wh8FQbiTt8pNcKJunY/MzWUXrm6mCTk6c5/wJB/0Ik4lAGijbgGebI4BPG4HNr89eh65A719uN+puFySL7MS4QO7M6uVBAK/qv/iJ0sSIn6Twi+vffp207OeuZZwGQez9pVD21pkgGVWCo54Q8DJvswiqDU4OHp16ePsMv0PGeqmk7iGCNOug0YR22yqsw6U5rGCwCsDddyuPyJ2al4HHaDg9U7vrAoRsqz5/LBVnQJcvVLiidrm0JVEY51mjcDCNEGG1Wl04GwtO5UxIFvFt/0dedEsIR59ezzpkwDpqDd7IjQHKC01yIdLODbCWbUwa6h0D4HRy8o7OZOi7JDr7JJ90Uls8TQ2Rc7JDGO0qVhYDqImGeaAR1fjlmsYpLA1LGz7Fv9En9WNtmZ66mCgubb2yjo/vPZYRths3R6X2yp2o252jMHssU+rM/hdChqaE5AW4JOnYuqUXpAgDoO7ay7MRgX3qP8TVDRRbtbbIUJ48558tSnt43yB3uHk9LK70YvfkpVWG5IPYF5W2nUXeRLph5J/JQjXV/7WsE3hmPhDAG2UudODbC/M9oSwuFVFOVaRJ8ksQj0Ykkg2FNzSEGFNwd8mAMACfLQSLJi9I+J5Ts6/tGewTgeaxygBPPGGz6Gqy6aYVeo6h2k+U8HqHHIkBhxWvqrMMPK1plKlIEh5seNVNOCHda6QVu9mEZzNXoqDERqadxGn5mHXjnovPLAhVpd+fLq0Y3zlNU7dG+f/XRuW8Fo3DDTnzvhMnHETnNIIK+0s0JngT+sTQC6UiFnQ+BnwDv7CDjoa/jmoK3V8/ChUGIbhsMw7hHuV1Mof5L0pLdvPxF3LDARKa8Y2Qj1OXmSYZX9BM8fAxr8dFUTG9YjAwjUFJQMKY0xusiyk2o3W1MzbBlDbGc7QrDPCR3esSAAo7MQWyexr6jXKDyEcff1rQ9iggwge/ytSbVrvzByxUzI5nhI7UA6jf1/01uyipBhYXesaVHelW6bopkSaEYVg/F4zLU29Lm2Sl0v8GNBh7lllqu1m2KpW094NtVPLs9VTsFZUmWRKmiJitRaSdQCa/iwEXeoAU9UZxKKGktwwcDfybdlaeb3kF1p2q84wH/enudGmAMrFBsYlSaS/lpHlfkWtauLCZoczPXkR/rwgyYyvdUo5NQwDx8mw9JTvEX/QaYGwk8i3sJ+5R8hhm+84HKv8+W8adyUbuiSir+lp6SSiNhAYXBz0G6cDJ40w5qo2ii2XrJaaj/hlwe/kasr72u5GAqxDxz+Tp6NZPHYxAOwdVSlfQXQHVsTDMwv91s7wG2524c3TEn3aW4xrKMdI4Jc6iv3e97cAOiLkPGvsy76NMAdilA1zO8DA9AJiXLuo6vqjewpGAbWVywGnlmiW/TsEVFE4GTwZ4PrarlG9x5E/uUUqygcoHk8wwvQRgILGDa9smLK8//kHJoYTRj/olovqJbo6AGTPkoXMmNnI/ZNNs6uG8h675xHLT9VNMwnyr0xs42S9EgdbK3Z4OiaPOieoojcRV2pijMgmPUtjJZQeoxAVTF3P3Y54N8k40rI+FX0S8SaVGIFwUxMo47NnNDX10kf5QjKc3bOqTyv/q/xFLUXm64EVZs/iQZIGKGrN8zj4sCJw03QMPaDoRfSqPBRwoerK6rDMCUL++hbCZzBzw6BvZ5lzws8K506mJ0/CCGHbl3BNlHiDyjSdZVASLQYKK1Us6lNVh5jn8YaS9HpM54aRON0UmsVtxNTfgV3+1ih68zhW4COzpwOM8rBJXyjbB5PbJin2nOZCTZkkZWBe+WfoLztdqRoy1cQhnx+/UtjwOo9wYFhE+nfMgSy7Lw0QClXnI0dK68oM7zOE4taxxf08aDGtgam5Ib4yOnF+xXw65bn8BwX9OLG66OwLNnN4gOZ6NOdwOTPA8jAgUNYXiijY31HsSE7F2r5k+w82rPOPVXpYrZMlpXQIHxXn0LoqKSpfMReUQtSIjRRIWy92hSRWEO1oVQVXddzTbq53hNslkyaIumxjC8WXk1n/kPpZf09sxx8o5zR1uX69VFuqPva3ueTaa9/xJoLBVkBn9zvRV9/ZNDVG7R2km0n6KMW6K0Id3gLdoNeM+P5uN8m3MW0TUYrKFBP2M7MpZJiY6nIHsibro2c4M0DNxGu2H34YjTVGnw3DdhhtVe2edpueyv8n83qn6v+6lQlI2pY1xBn4F8CcqbYOaopbVfwsvj25t1Cl8MY81ky+xpS/FNzkj3cOHw+8s/2xfu3fCgQ2DbUiJKQO1ByE7TLnUDuZNOseciQ9bGLjVLWGiWoetg4tgsV2j4LnGjEFSnq+1uuFcGzYs0dNSPL/+Le50Dz6/8etE0PtojwZ46gEfh5KwfKj/EJa2k9VSGeCi0RunleF9uiwHdDRGWQzRav0o3RV68wLm2u1oyGa9n4fd9dNzDolV3xtutc9t1d7Z2pl4Mzd74DzwBbiFmzKhTvEgF2M9x9bqFBJ9q1776rynILDg7R20TcfiGdaQmDVJ6xgv/12mxCmf/X4w/Ugsx/XWBU0ypaw1o+D0hw+2Wbgjng/0BEm9jJu/Eq1Ie4wKLyJO8s7PH/5GGXJeMDqC6ZyVousDxka945hq5PXER1PeKdy6kvY81EGGbWpdH36PEpznM99mfVM1jbaOzakmSbtOfleqJEqclL+aqvd7gquRQBNxXy1hb0Pa0qMg1XlHUYou3Ua9tqbwb+yuhdniA1P5dzfS0/j5nGMmDxYpw0WP1xeCRC95AuEwvnUsRuQeLevHcyjoWGU2/8dVTS5jYY6/wyuiX0LwHUJklkIA9WhDMZfj7vBm4kfwIRozUN4N3Oa00lqW7RpWyRDv2RIE3JyTcjXmbz/5c10mU0+VJaMohdVOHl+CjKi8CAZtZ4NOHikRn+mTr/L89vCSXrMJByiNEuew/IfqDTK5Jq+4qBDmK8f0K/hUjRgRC1WWpJU4+PkWz5OFd8A1fmTVZkuLnx71GmcjhuDLOM3xCOiTkoPrwL/TccYBzLEHCCkZAneBx6qGXM4hgc/0F5HtX0ITngmNb+jGGckB6jCOPUVZaBYt93Hsl2fdbyzpDPTB6poeyP0pyrbfJzidEwZ4J5qgV3iKt5uaF25ZP71o2b1ealczdACh4rQ4pBnQFGP7WsQcd1JW3j7Eq1MuS45kasCKrXTmZaVsI/zyIVgYlFKC0coCjubEJoKAm9lB23HnEg2qcFz3FviwR/ZbNWTDQFF0MuU5vVh3wMbT2QwCPdKBsHZSZ7tOvkGopUftffWdwV7ITCM93rfc4b5ZB58xybbcAjEIswAUZS+UIBcKGm3GnFx59nZy/jOeqbl9FnFvrQyrl7wErgEV1CZd81x2FqgL8e/fOhXCLccsVE7TT+s+Iy5ONqh14wcfaLDJ/pFCMLMIoDgqrI5hnfJR8YBGW1GtRTE37mVAiaX4Al3SLh9h+DzxeKp+5T9gFD8ZKiy+AB+BQASyeD+1Xq6zxiRGA9oE2T+y7abbXLJfaInyvKG/KtnjjDYqFDpsX0es4Qobuue+PRPyv7NeTgUF7k78EI4ogRmvs2NjS0yyVdMyURARvNFqYH2mZeUcEdHdi2pDPTnTtw0ymEmRXBXXc1L89MNaBVIiMi9yuB2Kjt1lmJe/wx8URLIi1IKSOsYu0LYS3CWCLIv3bYveiY1c/jhGPEe6mctuuojSDAAlxheJJ278dT86vGZRjPtutm73KUgp6zvfWaByJC0gaMX/AqcA4DZmSIf7kEgToAf6mv++EEBqbcRxZmMYCOhn+W1gFiy/YA4vSxMbUb56va+Dkrf1pdyK19vV2pq+zapXq0gxxMucJqLYjEU9prNjanMd6RqDSkWupA2kfuLLcjrzCN4lSl1AhOvG0vz2e6Wws8oxLU8e3l+KkhO1j0jlX1qtW8MGk6fqhVWftLo/POqxo2UtMu/c9ffdtpmctdR2G+67v4nzZcXPhax4RCpt7vaW7B+d80Tev8Y3t/x27KsaMBdYKsSq47mZXONZAHOIuJb+FQrT2eEI9jWnbsNo2m4WDdh9BxiLGBp7koaSqMohDbLXbdfF0TyO2VT2nOpbaJmYs36qzrl03T8QzlPAjOvgEZ3w0scZBfBLy6Iye71ctTqO9adR5ddVEvaqyDKEN1/Qtq9dbaCROExGBJWtoGtfdMhw55dAaBM+6JEVSLmKdObDhTsJBv/AdapvjSCPQVr6MJIOP2rIC1Nj/cy1a+D5MVxWaathy7XSEEArQ5C+QTWOVG3QH5H4WTnzgebb3ceBigbCSCrAn1seebjDNgM1EFKgf0+brH9dyD9WvOkf6H6HxK1uDQgkjSVpMBd9ZM/tb+HC+LwPOg0SDgk9470BUh+DVN9vmR1Xgw7Uw32iBenllWmp1+GdwD0SzW9g2DltfehsXurkvnMxfIQo8bzfUrViZauQVuNH8LZ3NLr4iKTdaftLEPauzbkWcvUHIFPOWdKtziFX6Ci9UF0bC3d6cuE56CFVDg1YySFBOe9g/WIeJ1wCcZCjfApOasuAWqG2ps0R2jcn6PzrzTyXC7VZz+v8FB6c9CEJ/SCUz43jMeWXsM1CJAFyc4wzwN8job7CBecD4pcne4LUeSvrvPh+WTOTOBnD7GT83yTXwA/NTjmQ+yDXw9JxPxPcjaCrx7IcF8tqCJ6DrnB3Yi2G8YlLmZc8Cs+vMYDjsUUnhwsxZVaMXLqnZp35sCs4WHt2xiYLk6FpOVHm6KwIs7gSNMWBJ6LuFZW+zSNhxoKqGvFCHF13KF2z7vW8swvlFmsjF55s8Go0JUMz9qfvlZZ/gYsLQAN5SFeznIDlebUscQZqVcxut+2ffFFdrkr6zjCE+tacrLUZ9dbQm82I/Gf1ztXfFv6kOAdBgMPpN6jwbNK0CgvKznKEwqRICcU7RLrje+f1/AmcEPKw6MC0GanFi5SNzr4IIPF64q95nl/ktL6PPoQ2sKK9se9ofrtuG36UJJhwaG9qzI8Wq/B/SqAHxryUIrMEz11LwYmYwHUTkS0qn2AnQyl13D+QxTLmeBYe3xcBYl7nScihScrj0X248Gk/cm/gOgET+W9M5qn2yGxy2+wjcCbqXBKBgWUh9DO6VEY30xOwx7ebbM4fXvkTGeXVIP5vwCooGLSLnzlVjoTezejWDT6Rw+LdtYeTdYDrU9DgUflpLOvNp5WKjCvCwcfBrToIbLST8CX2AjMJY8tjHhP+LgVsoFSKZpe8HjrZl9MJOD1xaMJq/RWC3TOGp+1dj2cCiCfLKqX7riWz/xCY6l+s92opIBNBWNnwbm50xvj1km6Mb+ylLbPtlxE28Wn7ZDIEeN8PZQeyQAXAMUwUxqgIybDh3LkrlAx6Gqa2hA5SJU6GIoKcCoOMUCNzDJFYZYWmKmnrXjr6l98A+GVAcAMNhg1htSGVZnKzIGEVFrRyNAn+m2yhn8s6QNVFb59hHup37DiUgPrrriVtUQtt21FRgSF6JLica+RcWe6nNQqDTKDq3eWiIOb9D6gEX6z3JKp1BexOipwBhTvjjenweRY4z1cmFeVyvokFV7ATxV+4xxSlzComb4e7cDmrblzec/tsO9fDEte7QELE9dJTY1CJddUyTuZkp0nQqRHuVT9WtHAJygNlnzYZpFL1TrsGWmRNUyXuykqO6JOIztAWhGnPncV4a5puVZggGAoWR/DdXz+Q5IMv3XfjTVzoRIs1VzpWfPY4/9Z8sE0dYgv3oiJwB7kBxPtrjP2+R+ZFiV2FFgyBMCXS4nFYa5Iq+Q6XwD1qSZxRdkLa9p6TOasjHFFaadl/nvNcPK0jaoTngVwO2h3ABqbKxie7cLL9sbHFS5sL7prpCX25SF6S85+zB4S1GwfAFPYfQA+M9B9b3+iEmdk7WnIVY6Y5CCbOUJ7aKd9BmAK/vmPIruix1pfYrG1nudVwprdicZPWNRVR7mIL/obbjbNTSS3biK1I9BkfsnUljdzB6m7QjvDh0KLxKyJqS7i/mK8qzmlwSt6zM1H/68/jZVlJgKftYsUk+K+LEr8VZ4KJjsPOwJKYQ27Gy4am0jJHgIXD2iCmJrswOHiF0ZqsIZjB61SYKu+E/hF2K4k1b+nCDcqzgJmS5zRfOZrUm8AD4OVDvDNJXEWnK+aU0yEwYpCPWIMR5ukylCPXuO6qRrPQYD1XWOXd7XuGKHwLIzFfijIeyLEQO3R2+T8pOqNrKARWfH2G1RTDk8NKOd5nEn8AtDyP3i1RT3Bdr36rOtxdlDk+/ZDn1N76cas45OCWhUaA46ZPg7W/aZdc/7I9vSJcv3OU47pC60rafQ9fYP2Px4J61jSorlXaTRUz1V80YZ5MpnmeG6nHJlFWrxv70doYUR/HP8zyFeXxda9d/7uVwCLxCLpctmRi8iJw/DA+7nLazKJwDy17MV8Vn9AtfgDt8LPUIbNHDj2A9toGN+6aPTjoMC7V57nd/d3SBQIiJqSmBS34taItwg26q/CuOFvTjovp/GdsVJjz8I4udPXUDj8f9HrGYYP7JbZ1ui7h/smIS4tkSO62xzCSNHN8fSMRWcObjBsvvX7+NJ91rKFPbg48Ihww0deQgu0ijyPv7MYNOJBzX+4rVAEaLL++q/GlKdoH5cgT5ZWu4QhAdXuzzd8F2HOdezLwBjPLdZcG3WnmwnTJUCI0yz5kv97KQceJ0Fg++xEJ9WIh9x7CerhCCfCLkJEk4xYQfdQyP4VLeppslQ8KS87PhUZ8I6FvzreCkl3Zqb+TLvd8kmFsL8XrpGn/l/PCQ0dEXKiqj/oxoI1OB//vcPRaL7VebVxAx8flKWu5tZ5vgI7ZuGV5VU1s036VTuJY7NXZJMOsZ3ZB8YCstgtDkw6bwQGHhU99f32ddM8hO3lTGFozye+GEjz19bNyDQBUvwWWDUUlA08B46ZeX/8y1GZ7LhoHThyHHy5xWVmS5xeFf05yTZT+6hu7auri9N4DUkVWuuISPpc94kYVw08UztPBv8Kemv5gjl11Idkv5T2yhl4TFjEgp1U5ayu+4FkJoymh0IfGP91zIl+ug1JYPLyzE0LHMvmFdFYfg/NOI+FyTbwTGj5Pbg7vqUV7JZ3HCRSNfnN8DEcTLL7gEanq01uhkfjxnkbYTrOMMSJd023ZZlT131/798BtpoX+PRpb582yQbs/Kkd1zuZ9z4ZJ0ZSJ6UucU+iD1NY4fpGupJJiQEa6ecLWXjt7MHXQANg+S75pbfp89LttZtgmSa/Dk/re2lf0lKfhG4UeGqVNtAq77KsVtIZo7s4smWf4DpXu45/xLqQZWtHoXtugbgee0dRHDH1BS96Yd+/EX3MCTM7620Ai+a8uXFbahs5TEka2K6Gpu/NkfVVO7ArnTx+4jezQE4YDXS+DyZbubBGUPnmYqPLwWItWZTazNY+lVHNuXMJKSzpS6/f5JzRUci/RZ01zApFvmkEyuk9wPOkFtevrjV0yxz9OEmoQZBDV6spWaj0QkBBjBm4br2/sqGD8IQeTqXn9JMIFjv0cjJB+Sced0IlvdsWRSyNgKnM1JCjStoMJ416OL4mD7IwOyWkv33LP6d1bOz8SYK16bWq+yvPerxwuL6I9qFKFj11quMPJHlimvALakuJN8ndJEZ3LZnsHwGcrPlhVWABb8CJNAO83egqDfF0pbGEKZjOpGRPBpGHZn1eFazW+Wv4PYzpjReTXheyQb9tsfJ/pK1cw5emxmAybMC9ErtXt7a4dqmIJyZbrO4B6NcLLQU0r2oIrcWdfCHWvjQF1qu8VUPeinHgqmNrxqh+qIwuodETEe7YZTjC/7H8eQK+9TF4I6us5yDmSYspHEi97Gl/YcvJn9ewIIsz4ONSVSdIE+cRSNnhtXPwt9Fg9MnUSDJm8wVvpd8Opv0F3p/y2D/QF4UgCMbhkoQYlZJeL4m9AfmXiloDuRpJQ6R/NzUNHyPVtbVsRuOvTU87o50dmY+YSmoN+OyDaKjyq1jXkllTnE9elLisL13fXD8ohGHaFVaazV/wHL92nP+iRTXSC0X85nG1B/BP37h12exhD1fcp34RNNmlaH8WjOIuONrBi6hMsrn1Indp31H/CtaEKevvNP0nMgIQHn4GiON4geajSzrfXK3psN6CSXW2X5iM/7sjJdvAGDDQb49yLFbjQOl4t+FUGqXhJ5Er9Z/kPcdjXNbgVv8WWE3RqskcQ2HGO7sydcr9BM9A8VCsmlwnU0FMcGKi36Jq8W3BZIRk0GoJmvPi5g5brMHW6A2VLHcQo3O5Dr3DIAK8kJDosPCBLM7yqmClRJGez6Us+ZgGK/DQSKs9/017kg/OSzc1D/RzQ6kvPca2Z6QK16T5Z3q89NZN/BnoVKHwomNq0xsyS4uOA5bBdAvJp3ryxGHfB21j+DtzuElbTQxzieKadYPWxUUlTjaMNS7D2CFSNIg/NIzmR3hHqmkcOCyTSi2PXEOCtRVD1UULLmZeF5LNzKXxvxw08hXiwcV6SMRiCKB8DnOjY+58gJ0/u5wmA7Jhk2qne8c78cuZ5e8ktBa0XmwDiqBzL4ewnoBaiRn9gqUfX8KUDeDxLEvsNrSK2pgUFZDKw8mYPGfLky4Y+TgufmNtWS1MShE6X84b/Vd3JD4L7sJroUI4mdnTWPuKKVoTSEuN2j4QGDa4Up3LYbp3cinz7RlgMDqsNrF9SKPw+/vEQYMTViMSQeA/raf9HiqyAxPVT4tsJzW7AJzd6ixh13+FybttyNpMyu7+InT06VUzaApkGrQ4g5zzTF7DQknXnqXYHCzspUIDcrswPpGqcRH/eCNe88JS1qioAWdyW+nRFoky2vFXqAAZUTd3uSNLMqPw6MtLLM2i414wQq5ZRuYhOfBw2jjZo6uhZdRjAYeEdMPE8dH/Lc7WK1gXHe5BajQ01vjDS6BSSMKWsGQTjrCR3rqbd6wvM+tIEtkc5u+hzSu/Yy/0jdBrVA9j29ylBXMsF+EL+KvT6MMN/lNvoIYRZJT2OmI+at2qmgsBqiPojTvo2ivWwpJ2wI3QsIhG7NZt59I0sFJqqYD8bUbJL8Zayq9rhj44RqHrgWhHS+PK7154HqfM9Y7aQT4bnAFS0jHORbcHLDZrlj7XNgL9Ad66bTZlG94rp8EvOHN3phRb99docm6ebD4O1hwBNzvkgjEd2JjG5ElxqFrg8QWpH6cflVkJ0a1bZkhDX2cLX2EYn+D9JAtfcLYr2o224VMPfgJk+kH+HPERAzzabpEfj1wJnIMV97HmuFdsVT/CKNuYr1kPcUmEt8BPi5dguatoPIFBusatHWzpRKv4SMk8tFFABS6EiEuT8rCg0nI25fuhGqyd6EU+Yi0ygk167gT+M22EvzIH7qXimTo9EpdyYm6aSZSFQqhEXHEyMmk6X4N3uX8hv4c15Y5sQgiTZ/LVAdnybhO+MIuXSkA3zOFad/BpQbqb8oa6lifT3/734tXApqar5Twd9bIqd38zrkzCNyXRGA3YFTY+0+F3kvtAIpBPVy4Cl/w0gJwDlwvsSGVc5PQ3w3MwNTNtlFh6T0709XV8ejE8M4rluMkHZFhMnErkl/BY72Z+hFNBoSaggQnsxuMigh/tqjjOD6J1TZrnJ6xXJcxV5Q1/IEJTJEv6RJ/aeYbMQxqJLXyldj+Uqr81tsKVXRKbitgM2MhQ/OuODqIkNFwQ0UTh1v6ED0F9mzt2CEUDo9S3T4J2cWx8h+erKw8ntXM/IXEwVZzxLC332OmnZbGiEXtH+RxlBSrryesQjmeZtDC3OsCwJtqewXKthmI2ezcAL6jhkyxAA9aznvDS3qbS+eKtD0C939gwRYtUcc+RMAejHADNe9just+E2BhLAEGbV1BMbg9xIVb4mSi0hFnW9YkLzuZ/WTMfgPDfzx8O28E9xBYSyAk8st74Vn5vaNh47ohPbNNCugJrhdsUjLsQ4FjapGYRpjK4DDJNE5SGLNnkBVixsoyDbTfLsSfH2BzSP88DBa4poYYivWeJhRJeqQAxrKx3OCaDUakI8IzRV3e0udoaUSp1NxWHxb+YZu7FcYLL8lcOku0Nsk4Oos3eiGEjLTd4IbhYuuz3k8KdOsXLWgKxUFK2Yv/ND+aPkOgxx30uwSTnUpNVF9kgyDn3ek+8eP5shgCY4+JCyMRaWUI6xqhQPmrKI32LRbj5nr4Ca7e7JRQLfBnsM806K5qzh/1RRGWb1XgYzKsLijlROIZIRJ0Kt/rMvD5W7ApnXoEBW8dvp+6a6vq1y2Z5lvpkMhoQT17eHqxjA1TvuugcXSz/tUcxVlqG9/aA85KAZjWtrTS2wJAoQhv9VccCeveEBgXaA6oD+Mmx/6wwl+eWxJaOFB10DrSi2NyQKA1uU+5jeD4rJnPmOcQDKNQzYcVvnguK55Fhiwf5WydffipRSP4eVlUS3dw15ULTjfjjhvui7j6DtFvOBW39AQ2RRDGzK3xfHHN2YnhRlqzbn7t5dt4OCiyDhMc4Ng75pLTIhzdzPblkoQbBPVAfO8JNprhAHeny92avShv0kRHOmG5/JtrrMTzk1ODv9TTp4Z15x+8a3xf1pubvWZM9XY1OS7wkp80ONKS4j0/NvzvXtUsTZvVLZMKVus8PJDooeMVKI3l131+Q1i2+e1zbBcU9AuSa0UCw0zCmRZ/d6DzJd26gneicSxtdyEECeERf7mla9kGizGpWCR3uKIbGoYo862grULpFk1GziGcWt9AJrRrPr5I/Lp2jDBRkjvVcgqKYHBt0jaqiFS2+5t6ClOv10lotBSkhtHGalR7iA1WJU6SkgF5zS7Or1HSSWE0JNtW0dzr/kUYcsXyH2ruFM9BavV2NrILZjMiO74FaVieEdYqKrdGgiV6dhqlq0v2+Zq+wC2L9LnQcoAlpMqDHICDa0s6U6CNWnM3gqWrgC9IwNAxRDQx3Qaepj+sbTnVczWwVAdFW1mlDKldXo2vFh2QFqmNRm5nT2rKEyaTMI4pkyhBLvQ+yHQcGyzim16kWHb6B8riOx7bgtDkwFBvX9X5of2tL1fTsW02+4S/ekvsiXu4/l/9HHV2OvHtl7DOAhD+WG4KvTMCyuVHwOz5qYDXmib/hsA/O1q5KPTE9NO4u/rCh3KGH5ryzX9z61jUafjlkzJ9957X2JMHVkpqdlgp0EQdQrRvyEWuEDpBjc71iVdDSS+lIScJdAR2BnOn4TnaIe9GruIChK1X1buVWa+GeOCJ0/9ssdV3imiTxEogH79UlVvfNwZJbhl2SWd8VXCeR12rd8oeD0p9+5gn296qMdWnj0pc5zRmpLjFFy+Hs+lg0PudoZoZdX5bTVgSuLW9uxvOoyx6LdEfCv3mZIZ+XrIhduaF2M7qHCAaXpVRkusOCGoTKKs7xAESOMeNKhc8ltCZKK6sFnyyHNy0ey9UFECKnD5+FL+WIyN0duSS8E5xvHhWPCC2r3iOsOGw+zjysgHQo41jbF2AHPddoR0yrEYlKSdKxD78wd/DLNfIH2pdZaDY4DRSHHNsOX8sMx8Dw9DMFFZDispMKa4Qgk1op4RV5Pani3AF5TWArouAJWx/AL1DLkt/XAQn0Q20Vh/kHCbZUctesc8URWommtty+jjz5YhoQzWPRf6hbO/d6qrwg2HuRGBCgNKk9iFa6y+8yQe9yHfntv6K0h8/+/YmmPL/hTLYSMT5+CEYRXyRA0wTbYVanfvHDIjmm4JUsOtzJEKYnDalh+k9X9/051WkioZC8Q5Oos+f+4MpG0awtJvwvnuhukK1LHV6D7NsCJgjpRkzYHz0vg9KnZ28NaArFrZBr0XH4/sodhGgwwelcDyXOKA8TFF+xFH8UeTGzHm1OY+pGGX71hRdynBvPUT/zI3A3dEVrIrV7qrYAGs27u4FlovjxN5rVOkJwhhl0U4dcc6MkXCoYyLG38RHGbSOZ25bO61s3TcmgaSBeDX6O/ABtzO2Nm/PWATcFKfkag9WBfEsASbT6bOaUbq4XXEmdewm/B+4/nSscz9gMvzMMPF4aFPt/rfOaYikWsU1wTLmhvvFLLWgNgG4zTfPkXpjAaWcAttPfSN/xd4N8vQ4HLHKe0eMUrLGP2JGDJT7oeeHpRekqY3t26BWGsCesCmMkKhRmz2VYLci5YJbPPrA2y+gaDrWjB3ge/IZTuFx9Oes2BihjizZCyPykk8wEAEQ8mf1brPtv+s64s0TAsjVQhGsNl0RyWWReE/qx6305n0Usz10uNhyWEtLua9Kxt/Qq0k1HfH1YLjipvu8mFBgEyaLnGTGG8hjUSqasFcnSVOZLMSP5Oa4Qr+wZz5fdz/C5eroTmo8RLAc6SwswyeGbDisIWp4aHrvGy+OuBKLT+TAlYM5RoAq3GRImrwqyWjQWdU6yoLo8GESJXpJ9uEkf7sptfaqk2KBSFSCJQ/bn7zEUOe0fI2wUlzzz8of0G3oPqq7Lj3+uVW8BalekCnU7lDO38bz7mDmASBqvBX9xikalzOWf+gCPyaYTFkYRwvQa9fRdF549BwuXZ4MPdwEEzKXml3rW5xtxURG9aLmjbQd+jWE6YryCX/x3q/tdUKQmhV8gewpjabKO3iJLkKEV/xUpuniLFqMbPpS6YT+0QlvpgMrjiGxZa93CmwxiME59izBHkGsdZmTnHMuvmZu1ldAFHq7Az0mHl9DrM7C5MF5DGkR1dQAHo/feZ4vmXnChRSJWkzOnGxWKjbMgPbXcmk+sdAHREDooh8frpEMuBMw5VcUjMx6xoubp2Y2uVRKY+OETM0mBmW7jRASw7BEc/7n9A1kd9MHyWFktvbfTFEIHRKaLGYs1RvRKRFkDE6P5Sa9LecMTYRqvOjfAhnD8dnMeGOMDYD3q2Ie4x0oXSwIRG7MMzDP6AZb7xdv9k9G3ltyb9GlXrpj2dTAvebbjO1aHTo35PqQAveEzSC386SkvjG/mQes/MPpO7JW8wAWyIjtoRBRGj2qW96drUxMHyguzG6nL3yazsPK9iJ1k4xf18nszWIXwCxrxzN0TkNRdjCWmS5qrsZgDhciy1+bRtEGuebS32Cp1w15LbRDuKpX3S/WS2SSBFeHgYQR9OcYno5R+x1sQxRHRFs9ke52lixMvPW9PCSTzaZ6zNAkUNL/wBGzIB+jNxHZCiVqiyNzeraSvyxZg+SMGFLVswKL0BsskLsnTjQ6/EjIa8I1uKIKSzIcDXAw/MS4eEU3q8m4Jt/YbqcF2gwTOgcra9W4jb7mqbyqpAwBNAle7M2wC9HgewkE1G3xrw9S9rtrMSb22NhP2lLlcfMXNxaeNv7MYYlFN9WuLxMrDd9RAYn+KqxxE1aG1NVCmtZLSuCGS+w4PjOSM4rXb5EDveyu7oJjkMa0u4khZQYphAgtcVj/N6zj1hNo9PmUXEs7duGU5JdafePcuQQ4PFk0yQGN3Laj4uOZfy9Ur+PQuxFJFRiGAvoG8gS84u5DTG/9AZ5UzlvVywNrUK+8uqfUjijzuwQmwe3t4OQzK1vOqey7Q2LaECW2zyAl2UvGVz00TbBXoZ4SOxpIQm8pEPrMsEabGEvbQ3ggyHRQbj/Obewx2BWl42wz9fc2+yMFVTG1wGVC+4SqMwwzUlkduicSB7q6L2eVt7B4CwTquwRw7OTRLYRf/t2Tcsg/PnalSWiywuMjnYF44Qy0PIPDyuO/7SpxzLEtcLGvK3JR4F6qU7AucerDLdPJ2KlmnenTPpzQDriJQ2UAXWD6Vl/7huWyspgYSIJ96h98kLfsYIh3z6Mq8I6A1VgeTYsPuSIgzy85/ZshhHBBFBCFnMg2PxOfNYfrG+I1Qf7KFQtOkpk9P/DNd5cNbhE12FkcEiJY01eRh34kIHfoHCewzm7rE/g+O5Dzo5by+QTH/xjAounV9gwENDaglK2YUlBV52Hj0volHD3QSzwxb3KpSNI1DX9rrdWVrK2A5UPMMA6PvJoMs/vKmJPsaJmiJq/3r2PCnP+iKPkXkQXJiGeltfh14CY32SdpVrRxVhlb2E01VegUstPS9xDmIu9oP0xc3Lk9l7GyhsNAG2pttgkPlDHuYF/+WM8S5HeknM3K6NfZdK9mNlBlFP+pGnbiqOLxbRc9w0a91NTDu0hR6ik/KLfZgsJhSrB4rysS5Qe4vpjvwr2eyReWJEm5qHtd20xiSlJPhZ6GByfG73sbTFk9x+tqapv+NlDf7ue1otVj5lcyjXzfpT7zj5amksyo0nwedxFRko//wk0ihrPcAOCPx+Nd4/esR/mvYRUVMeK3Xn+fZLa4nOXa6+zcCrT3s7xCeQWdt0t+rRzEDrM4STAvWrFXpJdve14H1Y5NQoCEOWHJuPOOYFl3E1ICtnRe5BHEPK3TpH8y1nYNkztiTMwOZIbylNXTm2SgSAIphYFB/zd2ZUuxwT0eETGkkrtpfbPDKT3ix3l8U2a0qS8VOHf97AofVonbdO13tmTPoKL3kC6hhkZOf6RTPIHbFlhphKO/RBOWyA679fdPa2JdVqZTnYRSwGBugBEbu6BUqFiogoBVTmf9QiuzWsXbBjqzp6CnPGZtcYEUBl3o7sHJG6N5bmeJvvLZPHHpLFAOrTCgnwhYLiQlcegC2GwW5jivsGZUZSOJck8ArHaE4EKZRVt/pAmLiE4HqKTCMUpZUFVW6wnPe3mC+HJpaly0WUBNYGhlQsv0VtMi+JILFpcsPd6UuD/82+mno+agX7iocYLhVfz+KY4QaVsjtwRr3Nl/UuvahA/mJJkhcWHKEbTbMtPsowddFRiP1GsLjcoxfpfr2fQUZv2FWREcMP2zxiWq7sIJu/tj9TF4auHawNiWTLxSvH4i6if3Bj2c7pLdzlPmPxq8CpP+s6+XOO383O4wHfURYaLsrtt1ydcKG9c8mnpDZpSXQEUpPluQT3wvSHht4cEVpd3W+CHdKENii0ApZWsl0D8N21rozzPV+7WoA5LfmYbxZVHvwV5tmtSNZsB1KGFIfbBqZE1nqo0VA8l0cclhbEt96z/kXuisLGqa4O2IrdHNjvoN7c16KDO8+in1plEpHxrUYJ2/dBAPH9OyEYE+cwCfxOd02UsAe+4TiMszYl0fjyysX5K/AgM9BM1L/wOKM+WuCNeAwYRxZtSCXAKteTuYA8JVWt7TyIONBnpSvcf42UP7jw3D4OKlkUeGdG5eX2XIqwXLxOa64Ep2iVHjx37V9B73qz5/Vw6r/OYDp9RcgP3erxUXyRBMrkOwl/+PNQAKyRORfkS1+tb9Kcjf0y68LJilupJQnQ8Q3ENDXDNbg1AzCEV64XC1+1p6o9rMmtioR5UOpfx1B0p2sF72rUm0wAlfgp54Crl2KPBcDa2R/eFK6I9ydYqyDoUlR59sc4qS4vhqTv/RxUU1KNLUyWgNV1Jo4dJftgjQauDRpAQyHFD+JXpos6a8+PC75Hb4RkNgQV3905NZHrWn8A/iImbsP6FmmxcNzeBSXrMMskkX36P8rnHiyjKqTOQj093nTzO/2+E5osbuetGuwxZ8aZ4kMJLn5o/JkOJp8aGqwqouVnllCC9RPvDKR4xBuYJ9SZMK7Zg+0AWj0q0yjFj4X0l906x0ECyyrMXhbY39ZsP8y3u500EyCewAbSROS4xRx4f8RdNmpfaXWihwknqcI9MxtzPHmwA0mnNZN83aQ/Q1hpV6M1r2U6YDgZZVLBdpkoCAYna3G7gy9qhrHc28TljbFt0af+R55BiZeDRuSt0Idb+K0Zph9rnLlUGT0aHei/MW1hakdb9QvWOQpzFfsCELkCSEuxyJBR/K48NUY21zL842NKLQ4iZs38Z11Wt4QlrI7jVbILuf2w9Yr9oUwe2Duc3hTaC07z4/dr2Y5u1x6ffnIJ+cY27TGjLKPpDU69JSrsCMfXu2B0MkePHLF07iaLnnr5ezzSkNF2PySmdu6mw5gqtXPVVdpAVKN4q2y0HrvtQMWteA8mLasO0TI7H2K+OWxf+o/PNs/6LEHXDVuQh1CFsera9wZyTIs6Xx70UbrMUzuMDd+tJyYb5Hqp1LuZWk/6cE7YALAlWxzDdGOplIrUdygPkCrgZcznWNAZw5QPjnnKTkC0scPYizZjkRMPnrVEbX9jqbH4TTf1gj03y431GOJwkuJJvD1LjHj9SpKx/OkWnk4QUiKeYEHrvhl6Q6B4Yh5LUR5sxmcOf5yOv1tzXR2WM4cgzdwcurLiMDimDMJPuOE0fKhsInXmDS3g719kfRCOpNBQGK/bxddONDd6lfiY0C6H/jfC3Dy3Mm/3YS7/NIcsSWDpJtI3qwg9HUqueWe0kGFac00H6otlllnYrGOmDqyr5h2M2aQ6jPGc1BuulVleVRhVTybbhXisySlpsWDaAQuRHua5OB/kt3VXJalQnwMcJX1Bk0/bSgMNEnZmxDrCgS7sObh+eGyN6uNb+fJro+l7V7onIsonZBSABgT7hVP9/HtqmN/QA3A+rekbAeiYsrjkv2xaXmaYf8eyYm+JQ2ZNGVAaoybmAKUVmA/70izu36y0z0BJevcEPzViNUlMyEs2KrSns4daZSAWyoL2jphdpv5mvLAa1xR+bfdA1scgNlwr4mKzvVsntKLs8kckA9gJXdQJIYCa47sItyxJzHE5XbGtbnMo76S4URwl9Gx+OlVisFEGqzezUL4CC01ew5M2HUQo2oHZJfnz/Xax63A7Kcf4qokd2LGJIAwuSfl1q5yQgX+Dol7YguZOTAA/F0i/jAwiG4DkWGozQWcU17bRel8RmUt/sZ+gOCxr4BErgBQggYDqr5otUoJFau+tDXoPuFkdvkmag97WJzQmbjARD8WI4BKh6ce4eFLyIt7ZFWm8n/d9/8jfTaupYjEtgJhgJ0Sxjml3imC6lwZYQyhaDcHRcLizQRweLyTV5bdufykb6GJIF/LgyKiRQMmjvW3bE2l8CgdEb7O6AEQopzV+PmOz6IuiBzXnVvv4S+oJz6f+hirOvMpsrjbYExV7qLl93uDQun3dvwp6Xjn//qG3Dx9PV8cJnOfkA1XOXLP+r67JmuLFGVntujYUvBwoGtFfbwPF270obokBjwYqBt2/3Ths67sNfrlvanecTxuoyZJku4BwdhRYFkqQD7Lj0De1N5B9M6+z35LvBQ4PFt0WXns+EPeA9eKhtW1ThFOtzyV+OBcvGf9hSno47A6PhNbhqnY9NjCHLdtGe6+NW7awTHTa/Y72mAzcgqiNSoHnfYB8ibhM2ULmjo5aU7ZmldoxV7rq1eAS9FlewAKl1QDevZ9xR7gFj2BSl5UTQiaApQmyDTUr9YoX8p7G6dnBqlJBtqNQpDGBQI0JW1XOSMTVHP6x/dX3wdgdYxJ9xNhZRRyfem2fcuh35mtISXNG47Pi+z7t9TnJKcDQHTuereWwnck6s4uIElu9TJxo4wXJBgV7oamLLP0uPTA1NR2Ukf3pos9tSc2IJcqMfmdoWAuvXQQSYiICMvL1hHi25+qU9NlheyiDb/TmGdKcPKX67TT5EhwEn7d55lJHUY+tQA1L+TjD+vQ2onsgMhI9wfCXy97hwdO3wr+010/oSbAQSAwoMRiOlDogIEFzbYy+YLZgrOwEzH6GNbzRNip/4z9zUdGb9SYb+lBdRQwFubJP4CdJ7eqTtTnTsvBIMV+qMb7kQzZUzVR+FrIj8fJuHxdLNEFXNZ+imO3FSFIRPOFzgicXYkPPH7wbImTbEq0Ri1W69SybgAUakq+XmGgJ1xbgFpkbs3ihIeD8di2kXsR7ccRy1h/63mthyRzLdoEDCvm3RslpWGRbb3I2V8JoHeuRo8pmna3RG/SMoVnJHO3zyWZ8EnExkhwBT75YoPssb72uF95mp43UW0gHKbhoxeZO541uvNxtUCo2xG5qjYRrSG25t81uLvV/WA61WfeM7lhdnB3hJFeqmjbNqUN573BZIOsBzyQciFXSMxa8Itp0tat+gq38bP1FWoH45Ibc8KTKehyklWioRtXNbXGuZi8FwaVLOR/SvFFtEQpRvcKY+cMYLHWvtauppJv+3eWjbLTflfADzNqdvBTAwYsFtGstS/tC95zcykWFvb4zpdwT/LxbCYuqjRsJ/jGpk+EYng5vWfBBSF5O/7qgf0X/dAq6fnFYHKK6Rhz3bm91bV250Tl6NVwFKo++YddYc8A7uHiMEzO2V0vpJNzI7Ala5NQHE6KoSp0kupt+nIK8CxBPwzvPa7gLyF9Bkh0Nnf71rB3KJlpdsah08M2EW3pTwpQX81qW4R1Gzox735zigHVgTfbjtGElh99YTvVvjUnJdHN+wM+XLVF2786ZqCwj+jNrynUUFkTG5uMJoCVv/Um7CwdOF7uDgykfaixHnLArW2DMYsjPEUQF0IncmuA9ewkgr6CSI7Rqdr0qCZLlCYJVizY3QAckntvqo7BcbY9aCMXfR0h5NJ6CljXESJfDvk82KNwh73KjNvIRHBvinC1XngLVrD8mSK5ANb/tjOtJtLHponcTbHjeBodOM5B5mCiItExcK6W6OoSkxuAic2uerVBnvuEUsbxHLil0v3JWBZnt3NP9gL10NAqcM/MYx7+qxoQmqTMaRkMW0K0TnKdfZMtxA/+JRJ3Ar7JwdyB381bN9XsTb+tho7zk6xu/L/+JHBCPIyKVUe1zjzPq5pfwMj80ae4eqkHMuqkjUioPSRxg6bFcHiPm+3rQ7lGHOyh95+PHiKWtFUNoV4aSlm5logX9w3MRRDRZJahTuWR5LRVmDEK6/PAHXt/4fZYjkpCERpr09UszSSekoyKgGqYUqck9CtRNQgJ9PHitAaVepdBDJspw81r6QhB/ezhK/e+5lV0LtZrbPhosACFKy9UyeRgWBfTmPvO1EA0xlfJHL/jUEe1apaMkrcanlyrXXeZf2nrw9L9Uxa8T15G4EV8mSFbT5xN7tlWtfYb3thYkeo8fIO2Njp5SAz9p0x4CA80QHEFVj9LpOpsDACAgc08bOw2hPgREMHnZo08YoW2HM1AVz7aN62l1B7SkCBJ04wUhHD8Et5rzLW7vbJBDA5FRZvIGlck5S7WEvxheP+eixqbR+ru8QRdv8z8gYr3Ox1fTPpa+zfqJeXl7JTlmwepkSsoFDkjFeKMsqEz9GBUwAQx+KJzFZrxiVmVR7+vPkCkeyiovPwYbhSL2BtxpNlsGrrJ5v5xgcfXQgS0D+3wHNP3ONrG5nbkzKAiCFXyhSUgLyWz6tApNR9bg==" data-envelopes="W3sic2FsdCI6IlFsenlPTFE4THNud3lYVzV3aFhqSUE9PSIsIml2IjoiQjA4ZEpLT2hTTHovb0NXdCIsIndyYXBwZWQiOiJRU0x5QTZDV2g3SVZFd2JqclB0NWIyRDFyc1c3MGZZa0VjbWh5TkZZa3ozeXJvUnhCVnRGckcvd3dmd0FMUlVFIn0seyJzYWx0IjoiOGlwWDF2NW9NblBUalE5aGovS1M2QT09IiwiaXYiOiJlcjc0UElGVkR0elVCUnMvIiwid3JhcHBlZCI6IjU4Wk53K0xHVlhkMHNvbTlBKzJSL2FWRFQxQTd6L1dxTFZXZ3pRSjl6QUZsMmttOWQ4elVEcHZwd295c2VXbGoifSx7InNhbHQiOiJOWjYvcXV1OGZlQmRoQU9WNUswa0RnPT0iLCJpdiI6IlN1ZGZySHJyc0ovNzFHbkEiLCJ3cmFwcGVkIjoiS2ZWZnc3THNmTHJMeHFKbzJFOE1zcDZMRHJnNkJnZzBmY2o4RG0xSktFdk4wRVlRZmJoQ2Z1M2U1UWtzV1U2OSJ9LHsic2FsdCI6IlNCRG9CeHI2d2M4YWFHQmJWWW05U2c9PSIsIml2IjoibUQyWVZoU2RpWXB0VDFuZiIsIndyYXBwZWQiOiJiV29tQ0k0WktzYkpRTnJRZFpYMTJUZ0NLbHJ6OUpqZXdmTkFhZkRPanVVeDBPQnlvdHk1UkhDa2c1TUlzeUN4In1d" data-tier-name="☕ 咖啡會員" style="--tier-color: #16a34a" data-ttl-days="1">  <div class="secret-prompt">    <div class="secret-lock" aria-hidden="true">🔒</div>    <div class="secret-title">這段內容已加密</div>    <div class="secret-hint">需要 <a href="/support" class="secret-tier-link" rel="noopener"><strong class="secret-tier" style="color: #16a34a">☕ 咖啡會員</strong></a> 或更高等級的密碼才能閱讀</div>    <div class="secret-form">      <input type="password" class="secret-input" placeholder="輸入密碼…" autocomplete="off" spellcheck="false">      <button type="button" class="secret-submit">解鎖</button>    </div>    <div class="secret-status" aria-live="polite"></div>    <div class="secret-cta">💡 年度公開密碼，到 <a href="/support">/support</a> 直接取得</div>  </div></div><p><img loading="lazy" src="/images/python/opencv/project-lineage-target/01.gif" alt="整合目標優先序選擇的主程式，Thread 2 呼叫 TargetSelector 選出目標後以紅框標記，UI 顯示當前鎖定目標的名稱與信心度"><br><em>圖：整合目標優先序選擇的主程式，Thread 2 呼叫 TargetSelector 選出目標後以紅框標記，UI 顯示當前鎖定目標的名稱與信心度</em></p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><h3 id="🎯-優先序與類別名稱"><a href="#🎯-優先序與類別名稱" class="headerlink" title="🎯 優先序與類別名稱"></a>🎯 優先序與類別名稱</h3><ul><li><strong>類別名稱要與訓練時完全一致</strong>：大小寫、底線要跟 <code>data.yaml</code> 或 <code>classes.txt</code> 對得上，拼錯只會變「未列出」落到最後。</li><li><strong>未列入優先清單的類別仍會被偵測</strong>，只是排序時放最後；全部都沒列也 OK，這時會退化成「距離畫面中心近者優先」。</li><li><strong>距離基準是畫面中心</strong>：若要改用角色實際位置（ROI 中心），把 <code>selector.select()</code> 的 <code>frame_center</code> 換成 <code>get_game_roi</code> 算出的中心即可。</li></ul><h3 id="🧵-與-Part-3-的熱切換共存"><a href="#🧵-與-Part-3-的熱切換共存" class="headerlink" title="🧵 與 Part 3 的熱切換共存"></a>🧵 與 Part 3 的熱切換共存</h3><ul><li><strong>優先清單即時生效</strong>：<code>_poll</code> 每 200ms 從「優先類別」Entry 把字串解析成 list 寫回 <code>selector.priority</code>，執行中隨便改都下一幀生效，不用停啟。</li><li><strong><code>min_conf</code> 預設 0</strong>：<code>detector.conf</code> 已經先濾一次，這裡再濾一次會讓 UI 滑桿失去意義、目標也可能忽有忽無。想只鎖定很高信心的目標時才手動拉高。</li><li><strong>熱換模型時 target 不會殘留</strong>：Thread 2 每輪都重算 target；<code>detector.enabled=False</code> 或模型未載入時會直接 <code>state.update(target=None)</code>，預覽的目標 label 會立刻回到「—」。</li></ul><h3 id="❓️-新手常踩的雷"><a href="#❓️-新手常踩的雷" class="headerlink" title="❓️ 新手常踩的雷"></a>❓️ 新手常踩的雷</h3><ul><li><strong><code>selector.priority = new_list</code> 是整包換</strong>：字串解析的結果一次 assign，CPython 下是原子操作；不要用 <code>.append()</code> 累加，否則熱切換時會越加越長。</li><li><strong><code>d is target</code> 用 identity 判相等</strong>：<code>annotate</code> 與 <code>render_grid</code> 都用 <code>d is target</code> 判斷是否為鎖定目標，所以不能 deep-copy <code>detections</code>，否則紅框永遠畫不出來。</li><li><strong>Entry 內容留空</strong>：<code>_parse_priority</code> 會回傳空 list，此時 <code>TargetSelector</code> 退化成純距離排序，不會崩潰。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p><strong>本篇也是整個天堂私服遊戲輔助系列的最終篇。</strong> 從 Part 1 的 tkinter GUI 與 HP/MP 血量偵測、Part 2 的 <code>PostMessage</code> 自動補藥、Part 3 的雙執行緒 YOLOv8 偵測整合，到本篇的目標優先序選擇，整套輔助程式的<strong>視覺辨識 → 狀態判讀 → 目標決策</strong>核心流程到這裡已經完整。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><p>至於更進一步的 <strong>自動攻擊怪物</strong> 與 <strong>自動撿取道具</strong>，因為涉及主動向遊戲下指令（攻擊熱鍵、滑鼠點擊），在法律與遊戲條款上相對敏感，本系列就<strong>不再實作</strong>。對有興趣延伸的讀者而言，前四篇打下的基礎已涵蓋核心技術 —— 鎖定後怎麼按下攻擊鍵、偵測到 <code>item</code> 類別後怎麼點擊，本質上都是把 <code>SharedState.target</code> / <code>SharedState.detections</code> 接到既有的 <code>PostMessage</code> 或滑鼠 API 上 —— 技術上不困難，<strong>是否實作與如何使用，請自行評估所在地區的法律與風險</strong>。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件</a><br><a href="https://docs.python.org/3/library/math.html#math.hypot" target="_blank" rel="external nofollow noopener noreferrer">Python math.hypot 文件</a><br><a href="https://docs.python.org/3/library/queue.html" target="_blank" rel="external nofollow noopener noreferrer">Python queue.Queue 文件</a><br><a href="https://docs.python.org/3/library/tkinter.ttk.html#ttk-combobox" target="_blank" rel="external nofollow noopener noreferrer">tkinter ttk.Combobox 文件</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;⚠️-免責聲明&quot;&gt;&lt;a href=&quot;#⚠️-免責聲明&quot; class=&quot;headerlink&quot; title=&quot;⚠️ 免責聲明&quot;&gt;&lt;/a&gt;⚠️ 免責聲明&lt;/h2&gt;&lt;p&gt;本文章內容&lt;strong&gt;僅供學術研究與電腦視覺技術學習之用途&lt;/strong&gt;，所有程式碼與技術說
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：天堂私服遊戲輔助（三）YOLOv8 怪物偵測整合</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260421-python-opencv-project-lineage-detection/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260421-python-opencv-project-lineage-detection/</id>
    <published>2026-04-21T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.559Z</updated>
    
    <content type="html"><![CDATA[<h2 id="⚠️-免責聲明"><a href="#⚠️-免責聲明" class="headerlink" title="⚠️ 免責聲明"></a>⚠️ 免責聲明</h2><p>本文章內容<strong>僅供學術研究與電腦視覺技術學習之用途</strong>，所有程式碼與技術說明均以教育目的為出發點。</p><ul><li>本文作者<font size="4"><font color="red"><u><strong>不提供任何形式的輔助程式販售、散佈或商業服務</strong></u></font>。</font></li><li>本文所有範例程式<font size="4"><font color="red"><u><strong>僅限在自行架設的私有伺服器環境中測試</strong></u></font>，不得用於任何正式營運的線上遊戲伺服器。</font></li><li>使用遊戲輔助程式可能違反個別遊戲的使用者條款，並導致帳號封鎖、法律責任等後果，<font size="4"><font color="red"><u><strong>讀者須自行承擔一切相關風險與責任</strong></u></font>。</font></li><li>本文技術內容若被用於任何違法或損害他人利益之行為，<font size="4"><font color="red"><u><strong>作者概不負責</strong></u></font>。</font></li><li>私有伺服器的架設與使用涉及遊戲著作權相關法律問題，讀者應自行評估所在地區的法規，並確認於合法範圍內使用。</li></ul><h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260420-python-opencv-project-lineage-auto-potion"><strong>（二）自動補藥</strong></a> 中，我們完成了自動補藥功能，並透過 <code>PostMessage</code> 讓補藥熱鍵即使在背景也能送進目標視窗。</p><p>本篇加入 <strong>YOLOv8 怪物偵測</strong>，核心挑戰在於：YOLO 推論本身耗時，若直接塞進 Thread 1 會拖慢血量偵測的更新速率。解法是新增 <strong>Worker Thread 2</strong> 專門跑 YOLO，兩個執行緒透過 <code>queue.Queue</code> 溝通。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><p><strong>系列總覽：</strong></p><table><thead><tr><th>篇次</th><th>主題</th></tr></thead><tbody><tr><td><a href="/python-opencv-20260419-python-opencv-project-lineage-assistant">一</a></td><td>主程式 GUI 框架與 HP/MP 血量監控</td></tr><tr><td><a href="/python-opencv-20260420-python-opencv-project-lineage-auto-potion">二</a></td><td>自動補藥</td></tr><tr><td><strong>本篇（三）</strong></td><td>YOLOv8 怪物偵測整合</td></tr><tr><td><a href="/python-opencv-20260422-python-opencv-project-lineage-target">四（最終篇）</a></td><td>目標優先序選擇</td></tr></tbody></table><h2 id="🎯-本篇目標"><a href="#🎯-本篇目標" class="headerlink" title="🎯 本篇目標"></a>🎯 本篇目標</h2><ul><li>建立 <code>detector.py</code>：封裝 YOLOv8 推論，並提供 <strong>live（畫 bbox）</strong> 與 <strong>grid（扁平菱形小地圖）</strong> 兩種預覽模式</li><li>新增 <strong>Thread 2</strong>（<code>detection_loop</code>）專跑 YOLO，Thread 1 透過 <code>frame_q = queue.Queue(maxsize=2)</code> 丟舊幀給它，保證推論永遠跑最新畫面</li><li>UI 加入「怪物偵測設定」面板與即時預覽區塊，checkbox 與「🔄 載入模型」都可執行中熱切換</li><li><code>window_capture.py</code> 內建 <code>HUD_PCT_BY_SIZE</code> 查表自動判定 ROI；附錄提供 <code>calibrate_game_roi.py</code> 讓你擴充新解析度</li></ul><h2 id="🗂️-本篇新增檔案"><a href="#🗂️-本篇新增檔案" class="headerlink" title="🗂️ 本篇新增檔案"></a>🗂️ 本篇新增檔案</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">lineage_assistant&#x2F;</span><br><span class="line">├── main.py                   ← 更新（怪物偵測設定面板 + 預覽切換 + Thread 2）</span><br><span class="line">├── window_capture.py         ← 更新（加入 HUD_PCT_BY_SIZE 查表 &#x2F; get_game_roi &#x2F; get_tile_px）</span><br><span class="line">├── hp_monitor.py             ← 不變</span><br><span class="line">├── auto_potion.py            ← 不變</span><br><span class="line">├── detector.py               ← 新增（本篇重點，含 live &#x2F; grid 雙模式）</span><br><span class="line">├── calibrate_game_roi.py     ← 附錄（要支援新解析度時才跑）</span><br><span class="line">├── models&#x2F;</span><br><span class="line">│   └── lineage_detector.pt</span><br><span class="line">└── config.json</span><br></pre></td></tr></table></figure><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install ultralytics pillow</span><br></pre></td></tr></table></figure><blockquote><p><code>pywin32</code>、<code>opencv-python</code>、<code>numpy</code> 在前兩篇已安裝，不需要重複裝。</p></blockquote><h2 id="📁-模型檔案從哪來"><a href="#📁-模型檔案從哪來" class="headerlink" title="📁 模型檔案從哪來"></a>📁 模型檔案從哪來</h2><p>本篇用到的 <code>models/lineage_detector.pt</code>，就是我們在 <a href="/python-opencv-20260418-python-opencv-project-lineage-yolov8"><strong>天堂私服 YOLOv8 物件偵測實戰</strong></a> 中一路從「私服截圖蒐集 → LabelImg 標記 → YOLOv8 訓練」產出的那顆 <code>runs/detect/lineage_detector/weights/best.pt</code>。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><p>只要把它<strong>複製或搬到</strong>專案的 <code>models/</code> 目錄下即可：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">lineage_assistant&#x2F;</span><br><span class="line">└── models&#x2F;</span><br><span class="line">    └── lineage_detector.pt   ← 從 runs&#x2F;detect&#x2F;lineage_detector&#x2F;weights&#x2F;best.pt 搬過來</span><br></pre></td></tr></table></figure><blockquote><p>若你還沒訓練過自己的模型，請先回去看上一篇把訓練流程跑完。也可以先拿 <code>yolov8s.pt</code> 官方預訓練權重接上來驗證整條管線通不通，只是偵測結果會是 COCO 類別（person、car…）而非怪物。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/project-lineage-detection/detection-architecture.svg" alt="Python - 圖 1 (detection architecture)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-detection/detection-iso-grid.svg" alt="Python - 圖 2 (detection iso grid)">​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h2 id="🗺️-遊戲畫面-ROI-查表"><a href="#🗺️-遊戲畫面-ROI-查表" class="headerlink" title="🗺️ 遊戲畫面 ROI 查表"></a>🗺️ 遊戲畫面 ROI 查表</h2><p>天堂視窗並不是「拍到什麼都拿來算」：標題列、聊天框、小地圖、上方 HUD 這些區塊都不是真正的世界畫面，拿去推座標會錯。所以要把<strong>真正的遊戲畫面</strong>在整個視窗內的矩形切出來。</p><p>好消息是：<strong>Lineage 的內部解析度只有幾個固定選項</strong>（400×300、800×600、1200×900…），而且每種解析度配上相同的 Windows 標題列後，擷取到的視窗尺寸跟 HUD 佔比都是固定的。所以作法是：</p><ol><li>作者私下用小工具對每種解析度量一次 HUD 佔比</li><li><strong>把結果寫成查表常數放進 <code>window_capture.py</code></strong></li><li>程式啟動後依 <code>capture_window</code> 實際抓到的視窗大小查表，直接選對應 <code>HUD_PCT</code></li></ol><p>讀者不用自己校準 —— 除非你的遊戲解析度不在表內，才需要回去跑附錄的 <code>calibrate_game_roi.py</code> 補一筆。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h3 id="window-capture-py-新增常數與函式"><a href="#window-capture-py-新增常數與函式" class="headerlink" title="window_capture.py 新增常數與函式"></a>window_capture.py 新增常數與函式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># window_capture.py 底部新增（本篇新增）</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># ── Lineage 各解析度的 HUD 佔比查表 ──</span></span><br><span class="line"><span class="comment"># key 是 capture_window 實際抓到的 (win_w, win_h)（含標題列與邊框）</span></span><br><span class="line"><span class="comment"># value 是該解析度下 HUD 在上/下/左/右四邊各佔視窗的比例</span></span><br><span class="line">HUD_PCT_BY_SIZE = &#123;</span><br><span class="line">    (<span class="number">406</span>, <span class="number">329</span>): &#123; <span class="comment"># 遊戲內 400×300</span></span><br><span class="line">        <span class="string">"top"</span>: <span class="number">0.079</span>,</span><br><span class="line">        <span class="string">"bottom"</span>: <span class="number">0.185</span>,</span><br><span class="line">        <span class="string">"left"</span>: <span class="number">0.002</span>,</span><br><span class="line">        <span class="string">"right"</span>: <span class="number">0.000</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    (<span class="number">802</span>, <span class="number">627</span>): &#123;  <span class="comment"># 遊戲內 800×600</span></span><br><span class="line">        <span class="string">"top"</span>:    <span class="number">0.080</span>,</span><br><span class="line">        <span class="string">"bottom"</span>: <span class="number">0.220</span>,</span><br><span class="line">        <span class="string">"left"</span>:   <span class="number">0.000</span>,</span><br><span class="line">        <span class="string">"right"</span>:  <span class="number">0.000</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    (<span class="number">1206</span>, <span class="number">929</span>): &#123; <span class="comment"># 遊戲內 1200×900</span></span><br><span class="line">        <span class="string">"top"</span>: <span class="number">0.029</span>,</span><br><span class="line">        <span class="string">"bottom"</span>: <span class="number">0.195</span>,</span><br><span class="line">        <span class="string">"left"</span>: <span class="number">0.002</span>,</span><br><span class="line">        <span class="string">"right"</span>: <span class="number">0.000</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 視野可視範圍：Lineage 實測螢幕寬能裝 ±8 tile-widths、螢幕高能裝 ±10 tile-heights</span></span><br><span class="line"><span class="comment"># 也就是 roi_w = 2·8·DW = 16·DW、roi_h = 2·10·DH = 20·DH（不是標準 iso 2:1 而是偏扁）</span></span><br><span class="line">TILES_PER_HALF_WIDTH  = <span class="number">8</span>    <span class="comment"># 水平可視半徑（格）</span></span><br><span class="line">TILES_PER_HALF_HEIGHT = <span class="number">10</span>   <span class="comment"># 垂直可視半徑（格）</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_pick_hud</span><span class="params">(win_w: int, win_h: int)</span>:</span></span><br><span class="line">    <span class="string">"""依視窗尺寸從查表挑 HUD_PCT；找不到精確匹配就回傳面積最接近的一筆"""</span></span><br><span class="line">    <span class="keyword">if</span> (win_w, win_h) <span class="keyword">in</span> HUD_PCT_BY_SIZE:</span><br><span class="line">        <span class="keyword">return</span> HUD_PCT_BY_SIZE[(win_w, win_h)]</span><br><span class="line">    target_area = win_w * win_h</span><br><span class="line">    nearest = min(HUD_PCT_BY_SIZE.keys(),</span><br><span class="line">                  key=<span class="keyword">lambda</span> k: abs(k[<span class="number">0</span>] * k[<span class="number">1</span>] - target_area))</span><br><span class="line">    <span class="keyword">return</span> HUD_PCT_BY_SIZE[nearest]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_game_roi</span><span class="params">(win_w: int, win_h: int)</span>:</span></span><br><span class="line">    <span class="string">"""回傳 (x, y, w, h)：遊戲渲染畫面在整個視窗內的矩形"""</span></span><br><span class="line">    hud = _pick_hud(win_w, win_h)</span><br><span class="line">    t = int(win_h * hud[<span class="string">"top"</span>])</span><br><span class="line">    b = int(win_h * hud[<span class="string">"bottom"</span>])</span><br><span class="line">    l = int(win_w * hud[<span class="string">"left"</span>])</span><br><span class="line">    r = int(win_w * hud[<span class="string">"right"</span>])</span><br><span class="line">    <span class="keyword">return</span> (l, t, win_w - l - r, win_h - t - b)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_tile_px</span><span class="params">(win_w: int, win_h: int)</span>:</span></span><br><span class="line">    <span class="string">"""回傳一格地磚在畫面上的像素尺寸 (tw, th)</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">    Lineage 視野是「左右 8 格、上下 10 格」的扁長方形，所以：</span></span><br><span class="line"><span class="string">        DW = roi_w / 16（= 2·TILES_PER_HALF_WIDTH）</span></span><br><span class="line"><span class="string">        DH = roi_h /  20（= 2·TILES_PER_HALF_HEIGHT）</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    _, _, rw, rh = get_game_roi(win_w, win_h)</span><br><span class="line">    <span class="keyword">return</span> (rw / (<span class="number">2</span> * TILES_PER_HALF_WIDTH),</span><br><span class="line">            rh / (<span class="number">2</span> * TILES_PER_HALF_HEIGHT))</span><br></pre></td></tr></table></figure><blockquote><p>兩個 <code>TILES_PER_HALF_*</code> 是遊戲常數（不會因視窗大小而變）；換不同客戶端或視角縮放才需要改。</p></blockquote><p><strong>怎麼量 8 / 10？</strong> 在遊戲裡把一個不動的地標（例如怪物或 NPC）放在人物正右方一格，往正左方走到地標剛好在畫面右邊界為止，走的格數 +1 就是水平半徑；垂直半徑同理（放正下方、往正上方走）。作者實測 Lineage 預設視角：水平 8、垂直 10。</p><h3 id="附錄：新增解析度用的-calibrate-game-roi-py"><a href="#附錄：新增解析度用的-calibrate-game-roi-py" class="headerlink" title="附錄：新增解析度用的 calibrate_game_roi.py"></a>附錄：新增解析度用的 calibrate_game_roi.py</h3><p><code>HUD_PCT_BY_SIZE</code> 沒涵蓋到你的解析度時才需要，流程：選取視窗 → 擷取一幀 → <code>cv2.selectROI</code> 框出「真正的遊戲畫面」→ 換算成四邊百分比印出來，複製一個 key-value 對貼進 <code>HUD_PCT_BY_SIZE</code> 就行。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><details><summary>點此展開 <code>calibrate_game_roi.py</code> 完整程式碼</summary><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># calibrate_game_roi.py</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">新增解析度支援用：</span></span><br><span class="line"><span class="string">  1. 開啟遊戲視窗並設定成要支援的解析度（保持顯示中、不要最小化）</span></span><br><span class="line"><span class="string">  2. python calibrate_game_roi.py</span></span><br><span class="line"><span class="string">  3. 點擊遊戲視窗鎖定 → 拖曳框出遊戲畫面範圍 → Enter 確認</span></span><br><span class="line"><span class="string">  4. 複製印出的 key-value 貼到 window_capture.py 的 HUD_PCT_BY_SIZE 字典裡</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> win32api</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> window_capture <span class="keyword">import</span> capture_window, window_from_point</span><br><span class="line"></span><br><span class="line">VK_LBUTTON = <span class="number">0x01</span></span><br><span class="line">VK_ESCAPE  = <span class="number">0x1B</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">pick_window</span><span class="params">()</span>:</span></span><br><span class="line">    print(<span class="string">"🎯 請點擊目標遊戲視窗（ESC 取消）..."</span>)</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">if</span> win32api.GetAsyncKeyState(VK_ESCAPE) &amp; <span class="number">0x8000</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        <span class="keyword">if</span> win32api.GetAsyncKeyState(VK_LBUTTON) &amp; <span class="number">0x8000</span>:</span><br><span class="line">            x, y = win32api.GetCursorPos()</span><br><span class="line">            hwnd, title, _ = window_from_point(x, y)</span><br><span class="line">            <span class="keyword">return</span> hwnd, title</span><br><span class="line">        time.sleep(<span class="number">0.03</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span><span class="params">()</span>:</span></span><br><span class="line">    result = pick_window()</span><br><span class="line">    <span class="keyword">if</span> result <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        print(<span class="string">"已取消"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    hwnd, title = result</span><br><span class="line">    print(<span class="string">f"✓ 鎖定視窗：<span class="subst">&#123;title&#125;</span>  hwnd=<span class="subst">&#123;hwnd&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    frame = capture_window(hwnd)</span><br><span class="line">    <span class="keyword">if</span> frame <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        print(<span class="string">"擷取失敗（視窗可能被最小化）"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    win_h, win_w = frame.shape[:<span class="number">2</span>]</span><br><span class="line">    print(<span class="string">f"視窗大小（含標題列）：<span class="subst">&#123;win_w&#125;</span> x <span class="subst">&#123;win_h&#125;</span>"</span>)</span><br><span class="line">    print(<span class="string">"請拖曳框出「真正的遊戲畫面」"</span></span><br><span class="line">          <span class="string">"（排除標題列、聊天框、小地圖、上方 HUD），Enter 確認"</span>)</span><br><span class="line"></span><br><span class="line">    x, y, w, h = cv2.selectROI(<span class="string">"Calibrate Game ROI"</span>,</span><br><span class="line">                               frame, fromCenter=<span class="literal">False</span>, showCrosshair=<span class="literal">True</span>)</span><br><span class="line">    cv2.destroyAllWindows()</span><br><span class="line">    <span class="keyword">if</span> w == <span class="number">0</span> <span class="keyword">or</span> h == <span class="number">0</span>:</span><br><span class="line">        print(<span class="string">"未框選有效範圍，已取消"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    hud = &#123;</span><br><span class="line">        <span class="string">"top"</span>:    round(y / win_h, <span class="number">3</span>),</span><br><span class="line">        <span class="string">"bottom"</span>: round((win_h - y - h) / win_h, <span class="number">3</span>),</span><br><span class="line">        <span class="string">"left"</span>:   round(x / win_w, <span class="number">3</span>),</span><br><span class="line">        <span class="string">"right"</span>:  round((win_w - x - w) / win_w, <span class="number">3</span>),</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    print(<span class="string">"\n請把以下 key-value 加到 window_capture.py 的 HUD_PCT_BY_SIZE：\n"</span>)</span><br><span class="line">    print(<span class="string">f"    (<span class="subst">&#123;win_w&#125;</span>, <span class="subst">&#123;win_h&#125;</span>): &#123;&#123;"</span>)</span><br><span class="line">    <span class="keyword">for</span> k <span class="keyword">in</span> (<span class="string">"top"</span>, <span class="string">"bottom"</span>, <span class="string">"left"</span>, <span class="string">"right"</span>):</span><br><span class="line">        print(<span class="string">f'        "<span class="subst">&#123;k&#125;</span>": <span class="subst">&#123;hud[k]:<span class="number">.3</span>f&#125;</span>,'</span>)</span><br><span class="line">    print(<span class="string">"    &#125;,"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-lineage-detection/03.gif" alt="執行 `calibrate_game_roi.py`，點擊遊戲視窗後拖曳框出真正的遊戲畫面範圍，程式即印出可貼進 HUD_PCT_BY_SIZE 的 key-value 對"><br><em>圖：執行 <code>calibrate_game_roi.py</code>，點擊遊戲視窗後拖曳框出真正的遊戲畫面範圍，程式即印出可貼進 HUD_PCT_BY_SIZE 的 key-value 對</em></p></details><h2 id="💻-偵測模組：detector-py"><a href="#💻-偵測模組：detector-py" class="headerlink" title="💻 偵測模組：detector.py"></a>💻 偵測模組：detector.py</h2><p><code>Detector</code> 有兩個預覽模式：</p><ul><li><code>mode=&quot;live&quot;</code>：沿用原本的 <code>annotate</code>，在原畫面上畫 bbox（適合調 conf、debug 誤判）</li><li><code>mode=&quot;grid&quot;</code>：呼叫 <code>render_grid</code>，把偵測結果投影到以角色為中心的<strong>扁平等角菱形格</strong>（左右 ±8、上下 ±10，對應遊戲實際可視範圍；適合運行中快速看怪物相對方位）</li></ul><p>格子地圖的核心是<strong>等角（isometric）逆變換</strong>。天堂視角旋轉 45°，世界座標東向對應畫面右上，所以把偵測中心的像素偏移 <code>(dx, dy)</code> 換成世界格座標：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">tw, th &#x3D; get_tile_px(...)   # &#x3D; roi_w&#x2F;16, roi_h&#x2F;20（水平半徑 8、垂直半徑 10）</span><br><span class="line">wx &#x3D; dx &#x2F; tw − dy &#x2F; th      # 世界東向（畫面右上為正）</span><br><span class="line">wy &#x3D; dx &#x2F; tw + dy &#x2F; th      # 世界南向（畫面右下為正）</span><br></pre></td></tr></table></figure><p>四捨五入即為格子座標。判斷是否在視野內不是用方形 <code>|wx|≤N</code>、<code>|wy|≤N</code>，而是用<strong>菱形</strong>：<code>|wx+wy| ≤ 16</code>（對應螢幕 ±8 格寬）<strong>且</strong> <code>|wy-wx| ≤ 20</code>（對應螢幕 ±10 格高）。格子地圖也只畫這塊菱形裡的 cell，整個小地圖就會跟遊戲畫面一樣扁、佔用空間也小很多。</p><blockquote><p>⚠️ <code>tw</code> 和 <code>th</code> <strong>分母不同</strong>（16 vs 20）：Lineage 的螢幕可視範圍是左右 8 格、上下 10 格，所以每格在螢幕上是扁的（<code>DW:DH ≈ 2.3:1</code>，不是標準 iso 2:1）。把 <code>th</code> 當 <code>rh/16</code> 算會讓上下方向的偵測位置偏掉。</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># detector.py</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Dict, List, Literal, Optional</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> window_capture <span class="keyword">import</span> (get_game_roi, get_tile_px,</span><br><span class="line">                            TILES_PER_HALF_WIDTH, TILES_PER_HALF_HEIGHT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 菱形視野邊界（把螢幕上「左右 ±8 / 上下 ±10」換算回 iso 世界座標）</span></span><br><span class="line">VIEW_EW    = <span class="number">2</span> * TILES_PER_HALF_WIDTH    <span class="comment"># 東西邊界：|wx + wy| ≤ 16</span></span><br><span class="line">VIEW_NS    = <span class="number">2</span> * TILES_PER_HALF_HEIGHT   <span class="comment"># 南北邊界：|wy - wx| ≤ 20</span></span><br><span class="line">GRID_HALF  = (VIEW_EW + VIEW_NS) // <span class="number">2</span>    <span class="comment"># 迴圈迭代半徑（菱形 4 角 |wx|/|wy| 最大 = 18）</span></span><br><span class="line"></span><br><span class="line">CANVAS_W   = <span class="number">200</span>                         <span class="comment"># 小地圖畫布寬（視野寬 16·DW = 192，加一點邊距）</span></span><br><span class="line">CANVAS_H   = <span class="number">130</span>                         <span class="comment"># 小地圖畫布高（視野高 20·DH = 120，加一點邊距）</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Detector</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, model_path: str, conf: float = <span class="number">0.5</span>)</span>:</span></span><br><span class="line">        self.model   = YOLO(model_path)</span><br><span class="line">        self.conf    = conf</span><br><span class="line">        self.enabled = <span class="literal">True</span></span><br><span class="line">        self.mode: Literal[<span class="string">"live"</span>, <span class="string">"grid"</span>] = <span class="string">"live"</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">detect</span><span class="params">(self, frame)</span> -&gt; List[Dict]:</span></span><br><span class="line">        <span class="string">"""</span></span><br><span class="line"><span class="string">        回傳偵測結果，每筆為：</span></span><br><span class="line"><span class="string">        &#123;"name": str, "conf": float,</span></span><br><span class="line"><span class="string">         "box": (x1, y1, x2, y2), "center": (cx, cy)&#125;</span></span><br><span class="line"><span class="string">        """</span></span><br><span class="line">        results = self.model(frame, conf=self.conf, verbose=<span class="literal">False</span>)</span><br><span class="line">        detections = []</span><br><span class="line">        <span class="keyword">for</span> r <span class="keyword">in</span> results:</span><br><span class="line">            <span class="keyword">for</span> box <span class="keyword">in</span> r.boxes:</span><br><span class="line">                name = self.model.names[int(box.cls)]</span><br><span class="line">                c    = float(box.conf)</span><br><span class="line">                x1, y1, x2, y2 = map(int, box.xyxy[<span class="number">0</span>])</span><br><span class="line">                cx, cy = (x1 + x2) // <span class="number">2</span>, (y1 + y2) // <span class="number">2</span></span><br><span class="line">                detections.append(&#123;</span><br><span class="line">                    <span class="string">"name"</span>: name, <span class="string">"conf"</span>: c,</span><br><span class="line">                    <span class="string">"box"</span>: (x1, y1, x2, y2), <span class="string">"center"</span>: (cx, cy)</span><br><span class="line">                &#125;)</span><br><span class="line">        <span class="keyword">return</span> detections</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">render</span><span class="params">(self, frame, detections: List[Dict])</span>:</span></span><br><span class="line">        <span class="string">"""主執行緒呼叫入口：依 mode 派發到 annotate 或 render_grid"""</span></span><br><span class="line">        <span class="keyword">if</span> self.mode == <span class="string">"grid"</span>:</span><br><span class="line">            <span class="keyword">return</span> self.render_grid(frame, detections)</span><br><span class="line">        <span class="keyword">return</span> self.annotate(frame, detections)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">annotate</span><span class="params">(self, frame, detections: List[Dict],</span></span></span><br><span class="line"><span class="function"><span class="params">                 target: Optional[Dict] = None)</span>:</span></span><br><span class="line">        <span class="string">"""live 模式：在原畫面上繪製偵測框"""</span></span><br><span class="line">        out = frame.copy()</span><br><span class="line">        <span class="keyword">for</span> d <span class="keyword">in</span> detections:</span><br><span class="line">            x1, y1, x2, y2 = d[<span class="string">"box"</span>]</span><br><span class="line">            color = (<span class="number">0</span>, <span class="number">0</span>, <span class="number">220</span>) <span class="keyword">if</span> (target <span class="keyword">and</span> d <span class="keyword">is</span> target) <span class="keyword">else</span> (<span class="number">0</span>, <span class="number">220</span>, <span class="number">0</span>)</span><br><span class="line">            cv2.rectangle(out, (x1, y1), (x2, y2), color, <span class="number">2</span>)</span><br><span class="line">            label = <span class="string">f"<span class="subst">&#123;d[<span class="string">'name'</span>]&#125;</span> <span class="subst">&#123;d[<span class="string">'conf'</span>]:<span class="number">.2</span>f&#125;</span>"</span></span><br><span class="line">            cv2.putText(out, label, (x1, y1 - <span class="number">6</span>),</span><br><span class="line">                        cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.5</span>, color, <span class="number">1</span>)</span><br><span class="line">        <span class="keyword">return</span> out</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">render_grid</span><span class="params">(self, frame, detections: List[Dict])</span>:</span></span><br><span class="line">        <span class="string">"""grid 模式：以扁平等角菱形格呈現怪物相對方位（左右 ±8、上下 ±10 的遊戲實際可視範圍）"""</span></span><br><span class="line">        fh, fw = frame.shape[:<span class="number">2</span>]</span><br><span class="line">        rx, ry, rw, rh = get_game_roi(fw, fh)</span><br><span class="line">        px, py = rx + rw / <span class="number">2</span>, ry + rh / <span class="number">2</span>    <span class="comment"># 玩家像素座標（ROI 中心）</span></span><br><span class="line">        tw, th = get_tile_px(fw, fh)          <span class="comment"># 一格地磚像素：roi_w/16, roi_h/20（分母不同！）</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 偵測中心 → 世界格座標（只收視野菱形內的，避免畫到邊界外）</span></span><br><span class="line">        monsters = set()</span><br><span class="line">        <span class="keyword">for</span> d <span class="keyword">in</span> detections:</span><br><span class="line">            cx, cy = d[<span class="string">"center"</span>]</span><br><span class="line">            dx, dy = cx - px, cy - py</span><br><span class="line">            wx = round(dx / tw - dy / th)</span><br><span class="line">            wy = round(dx / tw + dy / th)</span><br><span class="line">            <span class="keyword">if</span> abs(wx + wy) &lt;= VIEW_EW <span class="keyword">and</span> abs(wy - wx) &lt;= VIEW_NS:</span><br><span class="line">                monsters.add((wx, wy))</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 畫布與一格菱形的外接矩形大小（小地圖統一用 iso 2:1，方便閱讀）</span></span><br><span class="line">        canvas = np.full((CANVAS_H, CANVAS_W, <span class="number">3</span>), <span class="number">26</span>, dtype=np.uint8)</span><br><span class="line">        ccx, ccy = CANVAS_W // <span class="number">2</span>, CANVAS_H // <span class="number">2</span></span><br><span class="line">        DW, DH   = <span class="number">12</span>, <span class="number">6</span></span><br><span class="line"></span><br><span class="line">        WHITE, BLACK  = (<span class="number">240</span>, <span class="number">240</span>, <span class="number">240</span>), (<span class="number">16</span>, <span class="number">16</span>, <span class="number">16</span>)</span><br><span class="line">        YELLOW, EDGE  = (<span class="number">0</span>, <span class="number">220</span>, <span class="number">255</span>), (<span class="number">95</span>, <span class="number">95</span>, <span class="number">115</span>)</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">diamond</span><span class="params">(wx, wy, fill, edge)</span>:</span></span><br><span class="line">            <span class="comment"># 世界格 → 畫布像素中心（iso 45° 投影）</span></span><br><span class="line">            cx = ccx + (wx + wy) * DW / <span class="number">2</span></span><br><span class="line">            cy = ccy + (wy - wx) * DH / <span class="number">2</span></span><br><span class="line">            pts = np.array([</span><br><span class="line">                [cx,         cy - DH / <span class="number">2</span>],</span><br><span class="line">                [cx + DW / <span class="number">2</span>, cy         ],</span><br><span class="line">                [cx,         cy + DH / <span class="number">2</span>],</span><br><span class="line">                [cx - DW / <span class="number">2</span>, cy         ],</span><br><span class="line">            ], dtype=np.int32)</span><br><span class="line">            <span class="keyword">if</span> fill <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                cv2.fillConvexPoly(canvas, pts, fill)</span><br><span class="line">            cv2.polylines(canvas, [pts], <span class="literal">True</span>, edge, <span class="number">1</span>, cv2.LINE_AA)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> wx <span class="keyword">in</span> range(-GRID_HALF, GRID_HALF + <span class="number">1</span>):</span><br><span class="line">            <span class="keyword">for</span> wy <span class="keyword">in</span> range(-GRID_HALF, GRID_HALF + <span class="number">1</span>):</span><br><span class="line">                <span class="comment"># 菱形裁切：只畫「遊戲實際可視範圍」內的格子</span></span><br><span class="line">                <span class="keyword">if</span> abs(wx + wy) &gt; VIEW_EW: <span class="keyword">continue</span>   <span class="comment"># 東西邊界（螢幕左右 ±8）</span></span><br><span class="line">                <span class="keyword">if</span> abs(wy - wx) &gt; VIEW_NS: <span class="keyword">continue</span>   <span class="comment"># 南北邊界（螢幕上下 ±10）</span></span><br><span class="line">                <span class="keyword">if</span> wx == <span class="number">0</span> <span class="keyword">and</span> wy == <span class="number">0</span>:</span><br><span class="line">                    diamond(wx, wy, BLACK, WHITE)       <span class="comment"># 玩家</span></span><br><span class="line">                <span class="keyword">elif</span> (wx, wy) <span class="keyword">in</span> monsters:</span><br><span class="line">                    diamond(wx, wy, YELLOW, EDGE)       <span class="comment"># 怪物</span></span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    diamond(wx, wy, <span class="literal">None</span>, EDGE)         <span class="comment"># 空格（只畫邊框）</span></span><br><span class="line">        <span class="keyword">return</span> canvas</span><br></pre></td></tr></table></figure><h2 id="💻-執行緒架構（本篇）"><a href="#💻-執行緒架構（本篇）" class="headerlink" title="💻 執行緒架構（本篇）"></a>💻 執行緒架構（本篇）</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Worker Thread 1 (monitor_loop)</span><br><span class="line">  └── capture_window(state.hwnd) → HPMonitor.read() → potion.check()</span><br><span class="line">                                 └── frame_q.put_nowait(frame)   ← 傳幀給 Thread 2</span><br><span class="line"></span><br><span class="line">Worker Thread 2 (detection_loop)</span><br><span class="line">  └── frame_q.get() → detector.detect()</span><br><span class="line">                    → detector.render()   ← 依 mode 派發到 annotate 或 render_grid</span><br><span class="line">                    → SharedState.detections &#x2F; preview</span><br><span class="line"></span><br><span class="line">Main Thread (tkinter)</span><br><span class="line">  └── root.after(200ms) → _poll → 讀 SharedState → 更新 HP&#x2F;MP + 預覽圖</span><br><span class="line">                                → 面板設定即時推進 AutoPotion &#x2F; Detector</span><br></pre></td></tr></table></figure><h2 id="💻-主程式：main-py（第三篇版本）"><a href="#💻-主程式：main-py（第三篇版本）" class="headerlink" title="💻 主程式：main.py（第三篇版本）"></a>💻 主程式：main.py（第三篇版本）</h2><div class="secret-block" data-key="supporter" data-iter="200000" data-content-iv="XNFgvDNxjxaxcO1q" data-content="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" data-envelopes="W3sic2FsdCI6Ing1RTBXWUFYdDgwNzlrQUFCZDFSbEE9PSIsIml2IjoiU1h5Y0pROEVMbTBJdEdOMSIsIndyYXBwZWQiOiJ6OVA3c2NRU3JQZnNUalNZRWFxMG4yZlkzWjJJVkU4OFQ4QkNZLzhOT293bEdVbW9uVHFlc3dKU3pyUng1RHBLIn0seyJzYWx0IjoidHc0ajBsQXFESG1tUHN2bitlL1JWZz09IiwiaXYiOiJyRnByQVVwNXFYejcxRTZ1Iiwid3JhcHBlZCI6IjhZU1VlaDZuYytXT0FCM2V6cWVEUXN5am02dWUxWEx4Y3ppdEE4dmJtWjhUdUVTa1FxRW15QlVIVFZDRC8wVUcifSx7InNhbHQiOiJpTUJ6NnNWcmoyUm8wVzNET1B1TkxRPT0iLCJpdiI6IlJkeThJc2lEanBFQnJPYkYiLCJ3cmFwcGVkIjoidDhuUlB1S3JXRElEcFp0RlFONXlaMTI4dlZ3dW1mMEFZZmkwendXS3VZWW1WSmtqanlQaWh4T3RJb1JOZHpsdyJ9LHsic2FsdCI6IlBmWkx0dUhXZkIwanRQRmp0UGJlUEE9PSIsIml2IjoiZjNMQ3pFTkR6bWhIS3cyNCIsIndyYXBwZWQiOiJzcXplUGZlQmFNcGc4K1VPVWNXWWM5c0xtblNwVWRiaGVqRGNlSE5ZeE9PNE5Yb2VHcE9iVG85NHpXRTd4emVuIn1d" data-tier-name="☕ 咖啡會員" style="--tier-color: #16a34a" data-ttl-days="1">  <div class="secret-prompt">    <div class="secret-lock" aria-hidden="true">🔒</div>    <div class="secret-title">這段內容已加密</div>    <div class="secret-hint">需要 <a href="/support" class="secret-tier-link" rel="noopener"><strong class="secret-tier" style="color: #16a34a">☕ 咖啡會員</strong></a> 或更高等級的密碼才能閱讀</div>    <div class="secret-form">      <input type="password" class="secret-input" placeholder="輸入密碼…" autocomplete="off" spellcheck="false">      <button type="button" class="secret-submit">解鎖</button>    </div>    <div class="secret-status" aria-live="polite"></div>    <div class="secret-cta">💡 年度公開密碼，到 <a href="/support">/support</a> 直接取得</div>  </div></div><p><img loading="lazy" src="/images/python/opencv/project-lineage-detection/01.gif" alt="雙執行緒架構，Thread 1 擷取畫面傳入 frame_q，Thread 2 跑 YOLOv8 推論後把 detections / preview 寫回 SharedState，主執行緒每 200ms 更新 tkinter 預覽視窗"><br><em>圖：雙執行緒架構，Thread 1 擷取畫面傳入 frame_q，Thread 2 跑 YOLOv8 推論後把 detections / preview 寫回 SharedState，主執行緒每 200ms 更新 tkinter 預覽視窗</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><h3 id="🧵-執行緒與熱切換"><a href="#🧵-執行緒與熱切換" class="headerlink" title="🧵 執行緒與熱切換"></a>🧵 執行緒與熱切換</h3><ul><li><strong><code>frame_q(maxsize=2)</code> 丟舊幀</strong>：佇列滿就先 <code>get_nowait()</code> 丟最舊一幀再放新幀，Thread 2 永遠跑最新畫面、不因推論慢而累積延遲。</li><li><strong>三種熱切換皆下一幀生效、不用停啟</strong>：（1）<code>啟用怪物偵測</code> checkbox 改 <code>detector.enabled</code>；（2）「🔄 載入」新建 <code>Detector(...)</code> 原子指派給 <code>self.detector</code>，Thread 2 透過 <code>lambda: self.detector</code> 下一輪自動吃新模型；（3）<code>conf</code> 滑桿由 <code>_poll</code> 每 200ms 寫回 <code>detector.conf</code>。</li><li><strong>載模型失敗不會崩潰</strong>：<code>_load_detector</code> 用 try/except 包住，路徑錯或檔案損毀只寫日誌、保留上一顆模型，監控與補藥照常運作；啟動時找不到預設模型只記 log，勾 checkbox 會提醒先按「🔄 載入」。</li></ul><h3 id="❓️-新手常踩的雷"><a href="#❓️-新手常踩的雷" class="headerlink" title="❓️ 新手常踩的雷"></a>❓️ 新手常踩的雷</h3><ul><li><strong><code>ImageTk.PhotoImage</code> 必須保留參考</strong>：存到 <code>self._tk_img</code> 才不會被 Python GC 回收，否則預覽 Label 會變空白。</li><li><strong>worker 掛掉要讓 UI 知道</strong>：兩條 worker 都用 <code>try/finally</code> + <code>try/except</code> 吞例外；<code>_poll</code> 偵測到都掛了會把 ▶/■ 切回「啟動」並在日誌提示。</li></ul><h3 id="🗺️-ROI-與格子地圖座標"><a href="#🗺️-ROI-與格子地圖座標" class="headerlink" title="🗺️ ROI 與格子地圖座標"></a>🗺️ ROI 與格子地圖座標</h3><ul><li><strong>查表 key 是「含標題列的視窗尺寸」</strong>：<code>capture_window</code> 抓到整個視窗（含標題列與邊框），所以 key 比遊戲內部解析度大 ~2×27 像素。若解析度不在表內，<code>_pick_hud</code> 會找面積最接近的當 fallback，但角標可能對不準 —— 建議跑 <code>calibrate_game_roi.py</code> 補一筆。</li><li><strong>Lineage 的 tile 是扁的</strong>：<code>get_tile_px</code> 回傳 <code>(roi_w/16, roi_h/20)</code>，<strong>分母不同</strong>（螢幕寬裝得下 16 個 tile-width、高只裝得下 20 個 tile-height，DW:DH ≈ 2.3:1）。若把 <code>th</code> 也寫成 <code>rh/16</code>，上下方向的偵測位置會偏掉。</li><li><strong>偵測與繪製都用菱形視野</strong>：<code>|wx+wy| ≤ 16</code> <strong>且</strong> <code>|wy-wx| ≤ 20</code>，對應螢幕上左右 ±8、上下 ±10 的可視範圍；超出菱形的偵測結果忽略、超出菱形的空格不畫。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>本篇完成了最核心的多執行緒分工：Thread 1 負責螢幕擷取與補藥，Thread 2 負責 YOLO 推論，主執行緒只做 UI，中間用 <code>frame_q</code> 與 <code>SharedState</code> 解耦。</p><p>下一篇將加入 <a href="/python-opencv-20260422-python-opencv-project-lineage-target"><strong>（四）目標優先序選擇</strong></a>，讓 Thread 2 在眾多偵測結果中挑出最優先攻擊的目標，並在預覽視窗中用紅框標記它。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​‌​‌​‌‌‌​‌​​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件</a><br><a href="https://pillow.readthedocs.io/en/stable/reference/ImageTk.html" target="_blank" rel="external nofollow noopener noreferrer">Pillow ImageTk 文件</a><br><a href="https://docs.python.org/3/library/queue.html" target="_blank" rel="external nofollow noopener noreferrer">Python queue.Queue 文件</a><br><a href="https://docs.python.org/3/library/tkinter.ttk.html#ttk-combobox" target="_blank" rel="external nofollow noopener noreferrer">tkinter ttk.Combobox</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;⚠️-免責聲明&quot;&gt;&lt;a href=&quot;#⚠️-免責聲明&quot; class=&quot;headerlink&quot; title=&quot;⚠️ 免責聲明&quot;&gt;&lt;/a&gt;⚠️ 免責聲明&lt;/h2&gt;&lt;p&gt;本文章內容&lt;strong&gt;僅供學術研究與電腦視覺技術學習之用途&lt;/strong&gt;，所有程式碼與技術說
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：天堂私服遊戲輔助（二）自動補藥</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260420-python-opencv-project-lineage-auto-potion/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260420-python-opencv-project-lineage-auto-potion/</id>
    <published>2026-04-20T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.559Z</updated>
    
    <content type="html"><![CDATA[<h2 id="⚠️-免責聲明"><a href="#⚠️-免責聲明" class="headerlink" title="⚠️ 免責聲明"></a>⚠️ 免責聲明</h2><p>本文章內容<strong>僅供學術研究與電腦視覺技術學習之用途</strong>，所有程式碼與技術說明均以教育目的為出發點。</p><ul><li>本文作者<font size="4"><font color="red"><u><strong>不提供任何形式的輔助程式販售、散佈或商業服務</strong></u></font>。</font></li><li>本文所有範例程式<font size="4"><font color="red"><u><strong>僅限在自行架設的私有伺服器環境中測試</strong></u></font>，不得用於任何正式營運的線上遊戲伺服器。</font></li><li>使用遊戲輔助程式可能違反個別遊戲的使用者條款，並導致帳號封鎖、法律責任等後果，<font size="4"><font color="red"><u><strong>讀者須自行承擔一切相關風險與責任</strong></u></font>。</font></li><li>本文技術內容若被用於任何違法或損害他人利益之行為，<font size="4"><font color="red"><u><strong>作者概不負責</strong></u></font>。</font></li><li>私有伺服器的架設與使用涉及遊戲著作權相關法律問題，讀者應自行評估所在地區的法規，並確認於合法範圍內使用。</li></ul><h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260419-python-opencv-project-lineage-assistant"><strong>（一）主程式 GUI 框架與 HP/MP 血量監控</strong></a> 中，我們建立了 tkinter 骨架，可以即時顯示 HP/MP 比例。</p><p>本篇加入 <strong>自動補藥</strong> 功能：當 HP 或 MP 低於設定閾值時，自動模擬按下補藥熱鍵，並新增設定面板讓使用者在 UI 上調整參數。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><p><strong>系列總覽：</strong></p><table><thead><tr><th>篇次</th><th>主題</th></tr></thead><tbody><tr><td><a href="/python-opencv-20260419-python-opencv-project-lineage-assistant">一</a></td><td>主程式 GUI 框架與 HP/MP 血量監控</td></tr><tr><td><strong>本篇（二）</strong></td><td>自動補藥</td></tr><tr><td><a href="/python-opencv-20260421-python-opencv-project-lineage-detection">三</a></td><td>YOLOv8 怪物偵測整合</td></tr><tr><td><a href="/python-opencv-20260422-python-opencv-project-lineage-target">四（最終篇）</a></td><td>目標優先序選擇</td></tr></tbody></table><h2 id="🎯-本篇目標"><a href="#🎯-本篇目標" class="headerlink" title="🎯 本篇目標"></a>🎯 本篇目標</h2><ul><li>建立 <code>auto_potion.py</code>，封裝補藥邏輯與冷卻時間</li><li>用 <code>PostMessage</code> 把 <code>WM_KEYDOWN</code> / <code>WM_KEYUP</code> 直接送到目標視窗的 hwnd，<strong>背景也能按鍵</strong>（不搶焦點）</li><li>在 UI 新增：啟用開關、HP/MP 閾值滑桿、F5~F12 熱鍵下拉選單</li><li>Worker Thread 1 整合補藥邏輯，<code>main.py</code> 整體更新</li></ul><h2 id="🗂️-本篇新增檔案"><a href="#🗂️-本篇新增檔案" class="headerlink" title="🗂️ 本篇新增檔案"></a>🗂️ 本篇新增檔案</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">lineage_assistant&#x2F;</span><br><span class="line">├── main.py              ← 更新（加入補藥設定面板 + 傳入 AutoPotion）</span><br><span class="line">├── window_capture.py    ← 不變</span><br><span class="line">├── hp_monitor.py        ← 不變</span><br><span class="line">├── auto_potion.py       ← 新增（本篇重點）</span><br><span class="line">└── config.json</span><br></pre></td></tr></table></figure><blockquote><p>本篇沿用第一篇已安裝的 <code>pywin32</code>，不需要額外套件。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/project-lineage-auto-potion/auto-potion-flow.svg" alt="Python - 圖 1 (auto potion flow)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-auto-potion/auto-potion-architecture.svg" alt="Python - 圖 2 (auto potion architecture)">​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h2 id="💻-自動補藥模組：auto-potion-py"><a href="#💻-自動補藥模組：auto-potion-py" class="headerlink" title="💻 自動補藥模組：auto_potion.py"></a>💻 自動補藥模組：auto_potion.py</h2><p>設計重點：</p><ul><li><code>check(hp, mp)</code> 由 Worker Thread 1 呼叫，不能操作 tkinter 元件</li><li>使用 <code>PostMessage</code> 把 <code>WM_KEYDOWN</code> / <code>WM_KEYUP</code> 送到目標 hwnd，<strong>視窗可在背景</strong>；不像 <code>pyautogui.press()</code> 只會送到有焦點的視窗</li><li>使用 <code>log_fn</code> callback 把訊息傳回主執行緒的 <code>log_q</code></li><li><code>cooldown</code> 防止連按，避免瞬間消耗大量藥水</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># auto_potion.py</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> Callable, Optional</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> win32api</span><br><span class="line"><span class="keyword">import</span> win32con</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">VK_MAP = &#123;</span><br><span class="line">    <span class="string">"f1"</span>:  win32con.VK_F1,  <span class="string">"f2"</span>:  win32con.VK_F2,</span><br><span class="line">    <span class="string">"f3"</span>:  win32con.VK_F3,  <span class="string">"f4"</span>:  win32con.VK_F4,</span><br><span class="line">    <span class="string">"f5"</span>:  win32con.VK_F5,  <span class="string">"f6"</span>:  win32con.VK_F6,</span><br><span class="line">    <span class="string">"f7"</span>:  win32con.VK_F7,  <span class="string">"f8"</span>:  win32con.VK_F8,</span><br><span class="line">    <span class="string">"f9"</span>:  win32con.VK_F9,  <span class="string">"f10"</span>: win32con.VK_F10,</span><br><span class="line">    <span class="string">"f11"</span>: win32con.VK_F11, <span class="string">"f12"</span>: win32con.VK_F12,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">post_key</span><span class="params">(hwnd: int, key: str)</span>:</span></span><br><span class="line">    <span class="string">"""把 WM_KEYDOWN/UP PostMessage 到目標 hwnd，不需視窗在前景"""</span></span><br><span class="line">    vk = VK_MAP.get(key.lower())</span><br><span class="line">    <span class="keyword">if</span> vk <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> <span class="keyword">not</span> hwnd:</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    scan        = win32api.MapVirtualKey(vk, <span class="number">0</span>)</span><br><span class="line">    lparam_down = (scan &lt;&lt; <span class="number">16</span>) | <span class="number">1</span></span><br><span class="line">    lparam_up   = (scan &lt;&lt; <span class="number">16</span>) | (<span class="number">1</span> | (<span class="number">1</span> &lt;&lt; <span class="number">30</span>) | (<span class="number">1</span> &lt;&lt; <span class="number">31</span>))</span><br><span class="line">    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, vk, lparam_down)</span><br><span class="line">    win32api.PostMessage(hwnd, win32con.WM_KEYUP,   vk, lparam_up)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AutoPotion</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self,</span></span></span><br><span class="line"><span class="function"><span class="params">                 hwnd: int,</span></span></span><br><span class="line"><span class="function"><span class="params">                 hp_threshold: float = <span class="number">0.6</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 mp_threshold: float = <span class="number">0.4</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 hp_key: str = <span class="string">"f5"</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 mp_key: str = <span class="string">"f6"</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 cooldown: float = <span class="number">1.5</span>,</span></span></span><br><span class="line"><span class="function"><span class="params">                 enabled: bool = True,</span></span></span><br><span class="line"><span class="function"><span class="params">                 log_fn: Optional[Callable] = None)</span>:</span></span><br><span class="line">        self.hwnd         = hwnd</span><br><span class="line">        self.hp_threshold = hp_threshold</span><br><span class="line">        self.mp_threshold = mp_threshold</span><br><span class="line">        self.hp_key       = hp_key</span><br><span class="line">        self.mp_key       = mp_key</span><br><span class="line">        self.cooldown     = cooldown</span><br><span class="line">        self.enabled      = enabled   <span class="comment"># 可由主執行緒即時切換</span></span><br><span class="line">        self._log         = log_fn <span class="keyword">or</span> (<span class="keyword">lambda</span> msg: <span class="literal">None</span>)</span><br><span class="line">        self._last_hp_t   = <span class="number">0.0</span></span><br><span class="line">        self._last_mp_t   = <span class="number">0.0</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">check</span><span class="params">(self, hp: float, mp: float)</span>:</span></span><br><span class="line">        <span class="string">"""根據當前 HP/MP 決定是否補藥"""</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> self.enabled:</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        now = time.time()</span><br><span class="line">        <span class="keyword">if</span> hp &lt; self.hp_threshold <span class="keyword">and</span> (now - self._last_hp_t) &gt;= self.cooldown:</span><br><span class="line">            post_key(self.hwnd, self.hp_key)</span><br><span class="line">            self._last_hp_t = now</span><br><span class="line">            self._log(</span><br><span class="line">                <span class="string">f"HP 補藥（<span class="subst">&#123;hp*<span class="number">100</span>:<span class="number">.0</span>f&#125;</span>% &lt; <span class="subst">&#123;self.hp_threshold*<span class="number">100</span>:<span class="number">.0</span>f&#125;</span>%）"</span></span><br><span class="line">                <span class="string">f"，按 [<span class="subst">&#123;self.hp_key.upper()&#125;</span>]"</span></span><br><span class="line">            )</span><br><span class="line">        <span class="keyword">if</span> mp &lt; self.mp_threshold <span class="keyword">and</span> (now - self._last_mp_t) &gt;= self.cooldown:</span><br><span class="line">            post_key(self.hwnd, self.mp_key)</span><br><span class="line">            self._last_mp_t = now</span><br><span class="line">            self._log(</span><br><span class="line">                <span class="string">f"MP 補藥（<span class="subst">&#123;mp*<span class="number">100</span>:<span class="number">.0</span>f&#125;</span>% &lt; <span class="subst">&#123;self.mp_threshold*<span class="number">100</span>:<span class="number">.0</span>f&#125;</span>%）"</span></span><br><span class="line">                <span class="string">f"，按 [<span class="subst">&#123;self.mp_key.upper()&#125;</span>]"</span></span><br><span class="line">            )</span><br></pre></td></tr></table></figure><h2 id="💻-主程式：main-py（第二篇版本）"><a href="#💻-主程式：main-py（第二篇版本）" class="headerlink" title="💻 主程式：main.py（第二篇版本）"></a>💻 主程式：main.py（第二篇版本）</h2><div class="secret-block" data-key="supporter" data-iter="200000" data-content-iv="UdJUILcbQW7eyZ6I" data-content="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" data-envelopes="W3sic2FsdCI6IkxseldESlFpTHByRjlWS1BtTmN2Z1E9PSIsIml2IjoiTDl1MlFoeG1uUTNkUnV3SyIsIndyYXBwZWQiOiJSaW5paWw2N2VQRXVDaTNTNnFKSVpHUVMxbGlENERzckhkYnBqUzV6MXA3cEkybDFOWHlodlRxaVo1bThsOTY0In0seyJzYWx0IjoiUVZrTkRKeitZZXJYSkpkeDhFWXJDQT09IiwiaXYiOiJIbWtDZERSVGtnb1RYSS8vIiwid3JhcHBlZCI6ImRHYUd4ajdGZ3BHNmFuVjBES25JaHBGcjI3RWRuSlBGcDRhUHVIVWxPOXVTdy90djY2dEQrTWFHS1oyNFBQMmcifSx7InNhbHQiOiI4KyszUW1UelA5c3Bxc0R6dmlLMTRnPT0iLCJpdiI6InQ0eFJnblQ4YnJxTEJuSVQiLCJ3cmFwcGVkIjoiSzliVVZKOWcySnNPeEdJYWtBNldmWXNUcUgydVF6bUNvZjRNampMRTVoNUtQQkcxSzlCcUxoUThRSWRFbE1pVSJ9LHsic2FsdCI6InYvbi9JUmZFKzhGR2xPdG1uelZLV1E9PSIsIml2IjoiSG5rUjRXWG91aDNIYkJrcCIsIndyYXBwZWQiOiI3VTFUZkVrdmJUYm9GQkFmTStIZCtzeERyY29tN25idGpXUWh1NS9BeGoxbWdJR0xhREpqVlJPbjRROGIwZ1lxIn1d" data-tier-name="☕ 咖啡會員" style="--tier-color: #16a34a" data-ttl-days="1">  <div class="secret-prompt">    <div class="secret-lock" aria-hidden="true">🔒</div>    <div class="secret-title">這段內容已加密</div>    <div class="secret-hint">需要 <a href="/support" class="secret-tier-link" rel="noopener"><strong class="secret-tier" style="color: #16a34a">☕ 咖啡會員</strong></a> 或更高等級的密碼才能閱讀</div>    <div class="secret-form">      <input type="password" class="secret-input" placeholder="輸入密碼…" autocomplete="off" spellcheck="false">      <button type="button" class="secret-submit">解鎖</button>    </div>    <div class="secret-status" aria-live="polite"></div>    <div class="secret-cta">💡 年度公開密碼，到 <a href="/support">/support</a> 直接取得</div>  </div></div><p><img loading="lazy" src="/images/python/opencv/project-lineage-auto-potion/01.gif" alt="整合自動補藥模組的完整主程式，UI 新增閾值滑桿與 F5~F12 熱鍵下拉選單，啟動時連同目標 hwnd 一起傳入 Worker Thread"><br><em>圖：整合自動補藥模組的完整主程式，UI 新增閾值滑桿與 F5~F12 熱鍵下拉選單，啟動時連同目標 hwnd 一起傳入 Worker Thread</em></p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>按鍵直接 PostMessage 到目標 hwnd</strong>：不搶焦點，你可以在別的視窗工作；按鍵只會進指定遊戲視窗。</li><li><strong>DirectInput 遊戲可能不吃 PostMessage</strong>：本篇鎖定以 <code>WM_KEYDOWN</code> / <code>WM_KEYUP</code> 接收熱鍵的傳統 Win32 遊戲視窗（如天堂 L1J 系列私服）；若遊戲採 DirectInput/Raw Input，要改用 <code>SendInput</code> 並把視窗帶到前景才能生效。</li><li><strong>冷卻時間 <code>cooldown=1.5</code> 秒</strong>：防止連按，依藥水生效速度調整。</li><li><strong>面板設定皆為即時生效</strong>：checkbox、HP/MP 閾值滑桿、F5~F12 下拉選單都由 <code>_poll</code> 每 200ms 推進 <code>AutoPotion</code>，調了就馬上套用。</li><li><strong><code>hwnd</code> 例外，是啟動時一次讀取</strong>：切換目標視窗要先停止、重新 🎯 選取、再啟動。</li><li><strong>執行緒不會再靜默死掉</strong>：<code>monitor_loop</code> 外層 <code>try/except/finally</code> 把 pywin32 例外吞進 log；<code>_poll</code> 偵測到執行緒掛掉會自動把 ▶ / ■ 按鈕切回「啟動」並提示看日誌。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>本篇的自動補藥功能是遊戲輔助的基礎，整個邏輯都在 Worker Thread 1 裡執行，與 UI 完全解耦。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><p>下一篇將加入 <a href="/python-opencv-20260421-python-opencv-project-lineage-detection"><strong>（三）YOLOv8 怪物偵測整合</strong></a>，新增 Worker Thread 2 專門負責 YOLO 推論，並在 UI 中嵌入即時偵測預覽視窗。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://github.com/mhammond/pywin32" target="_blank" rel="external nofollow noopener noreferrer">pywin32 GitHub</a><br><a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea" target="_blank" rel="external nofollow noopener noreferrer">PostMessage - Win32 API</a><br><a href="https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-keydown" target="_blank" rel="external nofollow noopener noreferrer">WM_KEYDOWN - Win32 API</a><br><a href="https://docs.python.org/3/library/tkinter.ttk.html#ttk-scale" target="_blank" rel="external nofollow noopener noreferrer">tkinter ttk.Scale</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​‌​​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​‌​‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌‌‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;⚠️-免責聲明&quot;&gt;&lt;a href=&quot;#⚠️-免責聲明&quot; class=&quot;headerlink&quot; title=&quot;⚠️ 免責聲明&quot;&gt;&lt;/a&gt;⚠️ 免責聲明&lt;/h2&gt;&lt;p&gt;本文章內容&lt;strong&gt;僅供學術研究與電腦視覺技術學習之用途&lt;/strong&gt;，所有程式碼與技術說
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：天堂私服遊戲輔助（一）主程式 GUI 框架與 HP/MP 血量監控</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260419-python-opencv-project-lineage-assistant/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260419-python-opencv-project-lineage-assistant/</id>
    <published>2026-04-19T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.558Z</updated>
    
    <content type="html"><![CDATA[<h2 id="⚠️-免責聲明"><a href="#⚠️-免責聲明" class="headerlink" title="⚠️ 免責聲明"></a>⚠️ 免責聲明</h2><p>本文章內容<strong>僅供學術研究與電腦視覺技術學習之用途</strong>，所有程式碼與技術說明均以教育目的為出發點。</p><ul><li>本文作者<font size="4"><font color="red"><u><strong>不提供任何形式的輔助程式販售、散佈或商業服務</strong></u></font>。</font></li><li>本文所有範例程式<font size="4"><font color="red"><u><strong>僅限在自行架設的私有伺服器環境中測試</strong></u></font>，不得用於任何正式營運的線上遊戲伺服器。</font></li><li>使用遊戲輔助程式可能違反個別遊戲的使用者條款，並導致帳號封鎖、法律責任等後果，<font size="4"><font color="red"><u><strong>讀者須自行承擔一切相關風險與責任</strong></u></font>。</font></li><li>本文技術內容若被用於任何違法或損害他人利益之行為，<font size="4"><font color="red"><u><strong>作者概不負責</strong></u></font>。</font></li><li>私有伺服器的架設與使用涉及遊戲著作權相關法律問題，讀者應自行評估所在地區的法規，並確認於合法範圍內使用。</li></ul><blockquote><p>本系列的核心目的是展示 <strong>GUI 設計、多執行緒架構、螢幕擷取、影像分析</strong> 等電腦視覺技術的整合應用，相同技術同樣適用於工廠監控、遠端桌面分析等正當場景。</p></blockquote><h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260418-python-opencv-project-lineage-yolov8"><strong>天堂私服 YOLOv8 物件偵測實戰</strong></a> 中，我們完成了 YOLOv8 怪物偵測模型的訓練與即時推論。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p><p>這個系列（共 5 篇）將把訓練好的模型整合進一套完整的遊戲輔助程式，以<strong>逐步增加功能</strong>的方式帶你理解整個系統架構，每篇都附上可直接執行的完整 <code>main.py</code>。</p><p><strong>系列總覽：</strong></p><table><thead><tr><th>篇次</th><th>主題</th><th>重點功能</th></tr></thead><tbody><tr><td><strong>本篇（一）</strong></td><td>主程式 GUI 框架與 HP/MP 血量監控</td><td>tkinter UI、螢幕擷取、血量偵測</td></tr><tr><td><a href="/python-opencv-20260420-python-opencv-project-lineage-auto-potion">二</a></td><td>自動補藥</td><td>閾值設定、熱鍵模擬、補藥邏輯</td></tr><tr><td><a href="/python-opencv-20260421-python-opencv-project-lineage-detection">三</a></td><td>YOLOv8 怪物偵測整合</td><td>偵測執行緒、即時預覽視窗</td></tr><tr><td><a href="/python-opencv-20260422-python-opencv-project-lineage-target">四（最終篇）</a></td><td>目標優先序選擇</td><td>優先類別、距離排序、目標鎖定</td></tr></tbody></table><h2 id="🎯-本篇目標"><a href="#🎯-本篇目標" class="headerlink" title="🎯 本篇目標"></a>🎯 本篇目標</h2><ul><li>建立 tkinter 暗色主題遊戲輔助視窗</li><li>實作「🎯 選取視窗」按鈕，讓使用者點擊即鎖定任一目標視窗（取得 hwnd、標題、PID）</li><li>用 pywin32 <code>PrintWindow</code> 擷取選定視窗畫面</li><li>用 HSV 色相範圍 + 最右側有色像素比例偵測 HP/MP 血量</li><li>在主程式內整合「⚙ 校準血條」按鈕，框選 HP / MP 血條位置</li><li>設計 <code>SharedState</code> + <code>threading.Lock</code> 的執行緒安全資料結構</li></ul><h2 id="🗂️-本篇專案結構"><a href="#🗂️-本篇專案結構" class="headerlink" title="🗂️ 本篇專案結構"></a>🗂️ 本篇專案結構</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">lineage_assistant&#x2F;</span><br><span class="line">├── main.py              ← 主程式：UI、視窗選取、血條校準（本篇完成）</span><br><span class="line">├── window_capture.py    ← 視窗擷取與滑鼠選窗模組（本篇完成）</span><br><span class="line">├── hp_monitor.py        ← HP&#x2F;MP 血量監控模組（本篇完成）</span><br><span class="line">└── config.json          ← 由主程式「⚙ 校準血條」按鈕寫入</span><br></pre></td></tr></table></figure><blockquote><p>後續每篇會在同一目錄中新增模組，並更新 <code>main.py</code>。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p></blockquote><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install opencv-python pywin32</span><br></pre></td></tr></table></figure><blockquote><p>💡 <code>pywin32</code> 提供 <code>win32gui</code>、<code>win32ui</code>、<code>win32api</code>、<code>win32process</code>，用來呼叫 <code>PrintWindow</code>、<code>WindowFromPoint</code>、<code>GetAsyncKeyState</code>、<code>GetWindowThreadProcessId</code> 等 Windows API。若 <code>import win32gui</code> 失敗，補跑一次 post-install：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m pywin32_postinstall -install</span><br></pre></td></tr></table></figure></blockquote><p><img loading="lazy" src="/images/python/opencv/project-lineage-assistant/flow.svg" alt="Python - 圖 1 (flow)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-assistant/architecture.svg" alt="Python - 圖 2 (architecture)"></p><h2 id="💻-視窗擷取模組：window-capture-py"><a href="#💻-視窗擷取模組：window-capture-py" class="headerlink" title="💻 視窗擷取模組：window_capture.py"></a>💻 視窗擷取模組：window_capture.py</h2><p>用 Windows <code>PrintWindow</code> API 擷取指定 hwnd 的視窗畫面，即使視窗被其他視窗遮擋也能拿到完整畫面（最小化除外）。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># window_capture.py</span></span><br><span class="line"><span class="keyword">from</span> ctypes <span class="keyword">import</span> windll</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> win32gui</span><br><span class="line"><span class="keyword">import</span> win32process</span><br><span class="line"><span class="keyword">import</span> win32ui</span><br><span class="line"></span><br><span class="line">GA_ROOT = <span class="number">2</span>   <span class="comment"># GetAncestor flag：取頂層視窗</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">window_from_point</span><span class="params">(x, y)</span>:</span></span><br><span class="line">    <span class="string">"""回傳 (x, y) 螢幕座標下的頂層視窗 (hwnd, title, pid)；找不到回傳 (None, '', 0)"""</span></span><br><span class="line">    hwnd = win32gui.WindowFromPoint((x, y))</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> hwnd:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span>, <span class="string">""</span>, <span class="number">0</span></span><br><span class="line">    hwnd = win32gui.GetAncestor(hwnd, GA_ROOT)</span><br><span class="line">    _, pid = win32process.GetWindowThreadProcessId(hwnd)</span><br><span class="line">    <span class="keyword">return</span> hwnd, win32gui.GetWindowText(hwnd), pid</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">capture_window</span><span class="params">(hwnd)</span>:</span></span><br><span class="line">    <span class="string">"""用 PrintWindow 擷取視窗畫面：被遮擋也能抓，最小化會拿到黑畫面"""</span></span><br><span class="line">    left, top, right, bottom = win32gui.GetWindowRect(hwnd)</span><br><span class="line">    w, h = right - left, bottom - top</span><br><span class="line">    <span class="keyword">if</span> w &lt;= <span class="number">0</span> <span class="keyword">or</span> h &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    hwnd_dc  = win32gui.GetWindowDC(hwnd)</span><br><span class="line">    mfc_dc   = win32ui.CreateDCFromHandle(hwnd_dc)</span><br><span class="line">    save_dc  = mfc_dc.CreateCompatibleDC()</span><br><span class="line">    save_bmp = win32ui.CreateBitmap()</span><br><span class="line">    save_bmp.CreateCompatibleBitmap(mfc_dc, w, h)</span><br><span class="line">    save_dc.SelectObject(save_bmp)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># flag=2 (PW_RENDERFULLCONTENT)：支援 DWM 合成畫面，對多數現代應用必要</span></span><br><span class="line">    result = windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), <span class="number">2</span>)</span><br><span class="line">    img = np.frombuffer(save_bmp.GetBitmapBits(<span class="literal">True</span>), dtype=np.uint8).reshape((h, w, <span class="number">4</span>))</span><br><span class="line"></span><br><span class="line">    win32gui.DeleteObject(save_bmp.GetHandle())</span><br><span class="line">    save_dc.DeleteDC()</span><br><span class="line">    mfc_dc.DeleteDC()</span><br><span class="line">    win32gui.ReleaseDC(hwnd, hwnd_dc)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> img[:, :, :<span class="number">3</span>].copy() <span class="keyword">if</span> result == <span class="number">1</span> <span class="keyword">else</span> <span class="literal">None</span>   <span class="comment"># BGRA → BGR</span></span><br></pre></td></tr></table></figure><h2 id="💻-血量監控模組：hp-monitor-py"><a href="#💻-血量監控模組：hp-monitor-py" class="headerlink" title="💻 血量監控模組：hp_monitor.py"></a>💻 血量監控模組：hp_monitor.py</h2><p><code>HPMonitor</code> 透過 <strong>HSV 色彩比例</strong> 推算血條填充：</p><ol><li>從畫面截出血條 ROI</li><li>轉換為 HSV 色彩空間</li><li>對目標色（HP 紅 / MP 藍）做閾值遮罩</li><li>找最右側有色像素欄位，除以總寬度得到填充比例（0.0 ~ 1.0）</li></ol><p>HP / MP 兩組 ROI 各自儲存在 <code>config.json</code>，可用「⚙ 校準血條」按鈕隨時重新框選。</p><blockquote><p>⚠️ HSV 的紅色色相橫跨 0 與 180 兩端，本範例只取 <code>H=0~10</code> 足以應付多數血條；若你的遊戲血條偏紫紅（<code>H &gt; 170</code>），要同時 <code>cv2.inRange</code> 兩段再用 <code>|</code>（OR）合併成一張遮罩。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p></blockquote><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># hp_monitor.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">CONFIG_FILE    = <span class="string">"config.json"</span></span><br><span class="line">DEFAULT_HP_ROI = (<span class="number">10</span>, <span class="number">10</span>, <span class="number">200</span>, <span class="number">20</span>)</span><br><span class="line">DEFAULT_MP_ROI = (<span class="number">10</span>, <span class="number">35</span>, <span class="number">200</span>, <span class="number">20</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HPMonitor</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></span><br><span class="line">        self.hp_roi = DEFAULT_HP_ROI</span><br><span class="line">        self.mp_roi = DEFAULT_MP_ROI</span><br><span class="line">        self._load_config()</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_load_config</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(CONFIG_FILE):</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        <span class="keyword">with</span> open(CONFIG_FILE) <span class="keyword">as</span> f:</span><br><span class="line">            cfg = json.load(f)</span><br><span class="line">        <span class="keyword">for</span> key <span class="keyword">in</span> (<span class="string">"hp_roi"</span>, <span class="string">"mp_roi"</span>):</span><br><span class="line">            <span class="keyword">if</span> key <span class="keyword">in</span> cfg:</span><br><span class="line">                setattr(self, key, tuple(cfg[key]))</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_color_ratio</span><span class="params">(self, frame, roi, lower, upper)</span> -&gt; float:</span></span><br><span class="line">        <span class="string">"""HSV 色相範圍遮罩 + 最右側有色像素欄 / 總寬度"""</span></span><br><span class="line">        x, y, w, h = roi</span><br><span class="line">        <span class="keyword">if</span> w == <span class="number">0</span> <span class="keyword">or</span> h == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0.0</span></span><br><span class="line">        hsv  = cv2.cvtColor(frame[y:y+h, x:x+w], cv2.COLOR_BGR2HSV)</span><br><span class="line">        mask = cv2.inRange(hsv, np.array(lower), np.array(upper))</span><br><span class="line">        cols = np.any(mask &gt; <span class="number">0</span>, axis=<span class="number">0</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> np.any(cols):</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0.0</span></span><br><span class="line">        <span class="keyword">return</span> min(<span class="number">1.0</span>, (int(np.max(np.where(cols))) + <span class="number">1</span>) / w)</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">read</span><span class="params">(self, frame)</span>:</span></span><br><span class="line">        hp = self._color_ratio(frame, self.hp_roi,</span><br><span class="line">                               [<span class="number">0</span>, <span class="number">100</span>, <span class="number">80</span>], [<span class="number">10</span>, <span class="number">255</span>, <span class="number">255</span>])</span><br><span class="line">        mp = self._color_ratio(frame, self.mp_roi,</span><br><span class="line">                               [<span class="number">100</span>, <span class="number">100</span>, <span class="number">80</span>], [<span class="number">130</span>, <span class="number">255</span>, <span class="number">255</span>])</span><br><span class="line">        <span class="keyword">return</span> hp, mp</span><br></pre></td></tr></table></figure><h2 id="💻-主程式：main-py"><a href="#💻-主程式：main-py" class="headerlink" title="💻 主程式：main.py"></a>💻 主程式：main.py</h2><div class="secret-block" data-key="supporter" data-iter="200000" data-content-iv="VSRlExn/xBMSB4ky" data-content="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" data-envelopes="W3sic2FsdCI6InhiWGg1aG1BbThxeXZqNDJiUFZWMkE9PSIsIml2IjoiWTBIbnpNNzhEV0lSWDhLVCIsIndyYXBwZWQiOiJmMHdJbTNhaFdvZUgwNzNsS2VPZGRNcE5rcXpjcU5GOHQ4VXFiRUtvZ0tBS3hZWXJwZTBWYzVPU1RDL0pjaUplIn0seyJzYWx0IjoiTllCcWJidFk4OU81MFBoOHMyQktVdz09IiwiaXYiOiJsOU52dTZJVUtYUlpCSlVjIiwid3JhcHBlZCI6IkJqUUtzZmpEbG4yaElQekhXQzZZMzBUeXhJQ29VSUZCN3RNckVUWmhNTHVuS1NwU2NjTkdKN09VWUJNd1ZoVzQifSx7InNhbHQiOiJSdlUzUjZ4d1ZWazlOQ2Z1cUV5Qy93PT0iLCJpdiI6Ind2Z25HOUswZmZkeXFQcXQiLCJ3cmFwcGVkIjoiSEtEZHAwSTZSZmI1dEZVcmFBcUVReVZEdGZWN05CVHlIbnNPSW5jRE5UWHZHa1A0U3FoZENGY3JjRFZXWjNxQyJ9LHsic2FsdCI6InIvai9WSmplL0o4UEhFY29FT2hSRWc9PSIsIml2IjoicDB0SWdNd0tiN3QycUphZyIsIndyYXBwZWQiOiJISG5hT1Q5a2JteVhFNkUxOWhCUXVTYko2b251MkFab2x4bXB1emZEMHZMVGtyRks1ZUl0TXlVa2ZLVnF0eTFZIn1d" data-tier-name="☕ 咖啡會員" style="--tier-color: #16a34a" data-ttl-days="1">  <div class="secret-prompt">    <div class="secret-lock" aria-hidden="true">🔒</div>    <div class="secret-title">這段內容已加密</div>    <div class="secret-hint">需要 <a href="/support" class="secret-tier-link" rel="noopener"><strong class="secret-tier" style="color: #16a34a">☕ 咖啡會員</strong></a> 或更高等級的密碼才能閱讀</div>    <div class="secret-form">      <input type="password" class="secret-input" placeholder="輸入密碼…" autocomplete="off" spellcheck="false">      <button type="button" class="secret-submit">解鎖</button>    </div>    <div class="secret-status" aria-live="polite"></div>    <div class="secret-cta">💡 年度公開密碼，到 <a href="/support">/support</a> 直接取得</div>  </div></div><p><img loading="lazy" src="/images/python/opencv/project-lineage-assistant/01.gif" alt="主程式按下「🎯 選取視窗」後滑鼠變十字，點擊遊戲視窗即鎖定 hwnd / 標題 / PID；再按「⚙ 校準血條」彈出 OpenCV 視窗框選 HP、MP 血條後自動寫回 config.json"><br><em>圖：主程式按下「🎯 選取視窗」後滑鼠變十字，點擊遊戲視窗即鎖定 hwnd / 標題 / PID；再按「⚙ 校準血條」彈出 OpenCV 視窗框選 HP、MP 血條後自動寫回 config.json</em></p><p><img loading="lazy" src="/images/python/opencv/project-lineage-assistant/02.gif" alt="以 tkinter 建立暗色主題輔助視窗，Worker Thread 即時擷取畫面並偵測 HP/MP，每 200ms 更新 UI 進度條與日誌"><br><em>圖：以 tkinter 建立暗色主題輔助視窗，Worker Thread 即時擷取畫面並偵測 HP/MP，每 200ms 更新 UI 進度條與日誌</em></p><h2 id="🚀-執行流程"><a href="#🚀-執行流程" class="headerlink" title="🚀 執行流程"></a>🚀 執行流程</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python main.py</span><br></pre></td></tr></table></figure><ol><li>點 <strong>🎯 選取視窗</strong> → 滑鼠變十字，點擊目標遊戲視窗即鎖定（hwnd、標題、PID）</li><li>點 <strong>⚙ 校準血條</strong> → 彈出 OpenCV 視窗，依序框選 HP 血條與 MP 魔力條（Enter 確認 / C 重選）</li><li>點 <strong>▶ 啟動</strong> → HP/MP 以 5fps 即時更新</li></ol><blockquote><p>下次啟動會自動讀取 <code>config.json</code> 裡已儲存的 ROI，不用重新校準。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p></blockquote><h2 id="⚠️-常見問題"><a href="#⚠️-常見問題" class="headerlink" title="⚠️ 常見問題"></a>⚠️ 常見問題</h2><ul><li><strong>血條偵測不準確</strong>：再按一次 <strong>⚙ 校準血條</strong> 重新框選，範圍要涵蓋整條血條但不超出 UI 邊框。</li><li><strong>HP 一直顯示 0%</strong>：調整 <code>hp_monitor.py</code> 的 H（色相）範圍，不同遊戲血條的紅 / 藍色調差異頗大。</li><li><strong>按 🎯 選取視窗 後點到錯的子視窗</strong>：程式已用 <code>GetAncestor(GA_ROOT)</code> 自動取頂層，若仍不正確就再按一次重選。</li><li><strong>按 🎯 選取視窗 沒反應</strong>：本功能用 <code>GetAsyncKeyState</code> 輪詢全域滑鼠，若被其他程式攔截（例如部分遠端桌面），改用滑鼠直接點實體視窗。</li><li><strong>畫面是黑的</strong>：視窗被最小化時 Windows 不 render 畫面，<code>PrintWindow</code> 拿不到內容；把視窗從工作列還原即可。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>本篇建立了整個系列的骨架：tkinter 暗色 UI、<code>SharedState</code> 執行緒安全架構、pywin32 視窗擷取，以及用 HSV 色彩比例偵測 HP/MP 的血量監控模組。</p><p>下一篇將加入 <a href="/python-opencv-20260420-python-opencv-project-lineage-auto-potion"><strong>（二）自動補藥</strong></a> 功能，當 HP 或 MP 低於設定閾值時自動按下補藥熱鍵，並為 UI 增加閾值與熱鍵設定面板。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌‌​‌​​</p><blockquote><p>註：以上參考了<br><a href="https://github.com/mhammond/pywin32" target="_blank" rel="external nofollow noopener noreferrer">pywin32 GitHub</a><br><a href="https://docs.python.org/3/library/tkinter.html" target="_blank" rel="external nofollow noopener noreferrer">tkinter 官方文件</a><br><a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-windowfrompoint" target="_blank" rel="external nofollow noopener noreferrer">WindowFromPoint - Win32 API</a><br><a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate" target="_blank" rel="external nofollow noopener noreferrer">GetAsyncKeyState - Win32 API</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;⚠️-免責聲明&quot;&gt;&lt;a href=&quot;#⚠️-免責聲明&quot; class=&quot;headerlink&quot; title=&quot;⚠️ 免責聲明&quot;&gt;&lt;/a&gt;⚠️ 免責聲明&lt;/h2&gt;&lt;p&gt;本文章內容&lt;strong&gt;僅供學術研究與電腦視覺技術學習之用途&lt;/strong&gt;，所有程式碼與技術說
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：天堂私服 YOLOv8 物件偵測實戰</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260418-python-opencv-project-lineage-yolov8/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260418-python-opencv-project-lineage-yolov8/</id>
    <published>2026-04-18T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.558Z</updated>
    
    <content type="html"><![CDATA[<h2 id="⚠️-免責聲明"><a href="#⚠️-免責聲明" class="headerlink" title="⚠️ 免責聲明"></a>⚠️ 免責聲明</h2><p>本文章內容<strong>僅供學術研究與電腦視覺技術學習之用途</strong>，所有程式碼與技術說明均以教育目的為出發點，用於探討資料蒐集、影像標記、YOLOv8 模型訓練與即時物件偵測等技術在實際場景中的應用方式。</p><ul><li>本文作者<font size="4"><font color="red"><u><strong>不提供任何形式的輔助程式販售、散佈或商業服務</strong></u></font>。</font></li><li>本文所有範例程式<font size="4"><font color="red"><u><strong>僅限在自行架設的私有伺服器環境中測試</strong></u></font>，不得用於任何正式營運的線上遊戲伺服器。</font></li><li>使用遊戲輔助程式可能違反個別遊戲的使用者條款，並導致帳號封鎖、法律責任等後果，<font size="4"><font color="red"><u><strong>讀者須自行承擔一切相關風險與責任</strong></u></font>。</font></li><li>本文技術內容若被用於任何違法或損害他人利益之行為，<font size="4"><font color="red"><u><strong>作者概不負責</strong></u></font>。</font></li><li>私有伺服器的架設與使用涉及遊戲著作權相關法律問題，讀者應自行評估所在地區的法規，並確認於合法範圍內使用。</li></ul><blockquote><p>本文的核心目的是展示 <strong>資料蒐集 → 標記 → YOLOv8 訓練 → 即時偵測</strong> 這套完整流程，私服環境僅作為一個可控、高效的資料來源範例。相同的流程同樣適用於工廠瑕疵偵測、倉儲物件辨識、醫療影像分析等正當場景。</p></blockquote><h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260417-python-opencv-project-mediapipe-gesture"><strong>MediaPipe 手勢控制應用</strong></a> 中，我們完成了手勢辨識的互動應用。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><p>這一篇是一個真實的遊戲輔助開發實戰：<strong>天堂私服 YOLOv8 物件偵測</strong>。</p><p>天堂私服的最大優勢是你能完全掌控遊戲環境：可以任意召喚怪物、調整地圖、控制光線與場景，讓資料蒐集變得極為高效。這個流程可以套用到任何你想偵測的遊戲物件。</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>用私服環境快速蒐集遊戲截圖資料集</li><li>使用 LabelImg 標記怪物、NPC、道具等物件</li><li>以 YOLOv8 訓練自訂偵測模型</li><li>即時擷取遊戲畫面並進行物件偵測</li></ul><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install ultralytics pygetwindow pywin32</span><br><span class="line">pip install labelImg   <span class="comment"># 標記工具</span></span><br></pre></td></tr></table></figure><blockquote><p>💡 <code>pywin32</code> 提供 <code>win32gui</code>、<code>win32ui</code>，用來呼叫 Windows <code>PrintWindow</code> API 擷取被遮擋的遊戲畫面。若 <code>import win32gui</code> 報錯，補跑一次 post-install：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m pywin32_postinstall -install</span><br></pre></td></tr></table></figure><p>最小化時 Windows 不會 render 畫面，仍無法擷取。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/project-lineage-yolov8/lineage-yolov8-flow.svg" alt="Python - 圖 1 (lineage yolov8 flow)"></p><h2 id="🧰-共用工具：視窗擷取模組"><a href="#🧰-共用工具：視窗擷取模組" class="headerlink" title="🧰 共用工具：視窗擷取模組"></a>🧰 共用工具：視窗擷取模組</h2><p>擷取視窗畫面的邏輯，蒐集與偵測兩個步驟都會用到，抽出來共用。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># window_capture.py</span></span><br><span class="line"><span class="keyword">from</span> ctypes <span class="keyword">import</span> windll</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> pygetwindow <span class="keyword">as</span> gw</span><br><span class="line"><span class="keyword">import</span> win32gui</span><br><span class="line"><span class="keyword">import</span> win32ui</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find_game_window</span><span class="params">(title_keyword)</span>:</span></span><br><span class="line">    <span class="string">"""回傳符合標題關鍵字、可見、未最小化的視窗；找不到回傳 None"""</span></span><br><span class="line">    <span class="keyword">for</span> w <span class="keyword">in</span> gw.getAllWindows():</span><br><span class="line">        <span class="keyword">if</span> (title_keyword.lower() <span class="keyword">in</span> w.title.lower()</span><br><span class="line">                <span class="keyword">and</span> w.visible <span class="keyword">and</span> <span class="keyword">not</span> w.isMinimized</span><br><span class="line">                <span class="keyword">and</span> w.width &gt; <span class="number">0</span> <span class="keyword">and</span> w.height &gt; <span class="number">0</span>):</span><br><span class="line">            <span class="keyword">return</span> w</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">list_visible_windows</span><span class="params">()</span>:</span></span><br><span class="line">    <span class="string">"""列出所有可見且有標題的視窗，方便診斷"""</span></span><br><span class="line">    <span class="keyword">return</span> [w.title <span class="keyword">for</span> w <span class="keyword">in</span> gw.getAllWindows() <span class="keyword">if</span> w.title.strip() <span class="keyword">and</span> w.visible]</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">capture_window</span><span class="params">(hwnd)</span>:</span></span><br><span class="line">    <span class="string">"""用 PrintWindow 擷取視窗畫面：被遮擋也能抓，最小化會拿到黑畫面"""</span></span><br><span class="line">    left, top, right, bottom = win32gui.GetWindowRect(hwnd)</span><br><span class="line">    w, h = right - left, bottom - top</span><br><span class="line">    <span class="keyword">if</span> w &lt;= <span class="number">0</span> <span class="keyword">or</span> h &lt;= <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    hwnd_dc  = win32gui.GetWindowDC(hwnd)</span><br><span class="line">    mfc_dc   = win32ui.CreateDCFromHandle(hwnd_dc)</span><br><span class="line">    save_dc  = mfc_dc.CreateCompatibleDC()</span><br><span class="line">    save_bmp = win32ui.CreateBitmap()</span><br><span class="line">    save_bmp.CreateCompatibleBitmap(mfc_dc, w, h)</span><br><span class="line">    save_dc.SelectObject(save_bmp)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># flag=2 (PW_RENDERFULLCONTENT)：支援 DWM 合成畫面，對多數現代應用必要</span></span><br><span class="line">    result = windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), <span class="number">2</span>)</span><br><span class="line">    img = np.frombuffer(save_bmp.GetBitmapBits(<span class="literal">True</span>), dtype=np.uint8).reshape((h, w, <span class="number">4</span>))</span><br><span class="line"></span><br><span class="line">    win32gui.DeleteObject(save_bmp.GetHandle())</span><br><span class="line">    save_dc.DeleteDC()</span><br><span class="line">    mfc_dc.DeleteDC()</span><br><span class="line">    win32gui.ReleaseDC(hwnd, hwnd_dc)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> img[:, :, :<span class="number">3</span>].copy() <span class="keyword">if</span> result == <span class="number">1</span> <span class="keyword">else</span> <span class="literal">None</span>   <span class="comment"># BGRA → BGR</span></span><br></pre></td></tr></table></figure><h2 id="💻-步驟一：自動蒐集遊戲截圖"><a href="#💻-步驟一：自動蒐集遊戲截圖" class="headerlink" title="💻 步驟一：自動蒐集遊戲截圖"></a>💻 步驟一：自動蒐集遊戲截圖</h2><p>在私服中固定地點、怪物附近定時截圖，快速建立訓練資料集。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># collect_screenshots.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">from</span> window_capture <span class="keyword">import</span> find_game_window, capture_window</span><br><span class="line"></span><br><span class="line">SAVE_DIR     = <span class="string">"dataset/raw_images"</span></span><br><span class="line">INTERVAL     = <span class="number">0.5</span>          <span class="comment"># 每隔 0.5 秒截一張</span></span><br><span class="line">TARGET       = <span class="number">500</span>          <span class="comment"># 目標張數</span></span><br><span class="line">WINDOW_TITLE = <span class="string">"Lineage"</span>    <span class="comment"># 遊戲視窗標題（部分符合即可）</span></span><br><span class="line"></span><br><span class="line">os.makedirs(SAVE_DIR, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">game_win = find_game_window(WINDOW_TITLE)</span><br><span class="line"><span class="keyword">if</span> game_win <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">    print(<span class="string">f"找不到『<span class="subst">&#123;WINDOW_TITLE&#125;</span>』視窗，請先開啟遊戲"</span>)</span><br><span class="line">    exit()</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"開始蒐集，目標 <span class="subst">&#123;TARGET&#125;</span> 張，間隔 <span class="subst">&#123;INTERVAL&#125;</span> 秒（Ctrl+C 停止）"</span>)</span><br><span class="line"></span><br><span class="line">count = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> count &lt; TARGET:</span><br><span class="line">    frame = capture_window(game_win._hWnd)</span><br><span class="line">    <span class="keyword">if</span> frame <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        time.sleep(INTERVAL)</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    cv2.imwrite(<span class="string">f"<span class="subst">&#123;SAVE_DIR&#125;</span>/<span class="subst">&#123;count:<span class="number">05</span>d&#125;</span>.jpg"</span>, frame)</span><br><span class="line">    count += <span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> count % <span class="number">50</span> == <span class="number">0</span>:</span><br><span class="line">        print(<span class="string">f"已蒐集：<span class="subst">&#123;count&#125;</span>/<span class="subst">&#123;TARGET&#125;</span>"</span>)</span><br><span class="line">    time.sleep(INTERVAL)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"完成！共 <span class="subst">&#123;count&#125;</span> 張，儲存於 <span class="subst">&#123;SAVE_DIR&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><blockquote><p>💡 <strong>私服蒐集技巧</strong>：</p><ul><li>在同一張地圖、同一區域定點蒐集，減少背景多樣性帶來的干擾</li><li>不同時間段（白天/夜間）各蒐集一批，讓模型適應光線變化</li><li>如果要偵測多種怪物，分別在各怪物出沒的地圖蒐集</li></ul></blockquote><h2 id="💻-步驟二：LabelImg-標記"><a href="#💻-步驟二：LabelImg-標記" class="headerlink" title="💻 步驟二：LabelImg 標記"></a>💻 步驟二：LabelImg 標記</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安裝後直接執行</span></span><br><span class="line">labelImg</span><br></pre></td></tr></table></figure><p><strong>標記流程：</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><ol><li><strong>Open Dir</strong> → 選擇 <code>dataset/raw_images/</code></li><li><strong>Change Save Dir</strong> → 選擇 <code>dataset/raw_labels/</code></li><li>左側切換格式為 <strong>YOLO</strong></li><li>按 <code>W</code> 畫框 → 輸入類別名稱（如 <code>orc</code>、<code>troll</code>、<code>item</code>）</li><li>按 <code>Ctrl+S</code> 儲存，<code>D</code> 切換下一張</li><li>開啟 <strong>Auto Save</strong> 可省略手動存檔</li></ol><p><strong>建議標記的類別（依需求調整）：</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">怪物類：orc, troll, dark_elf, zombie, ...</span><br><span class="line">NPC 類：npc_shop, npc_quest, ...</span><br><span class="line">道具類：item_weapon, item_armor, item_misc</span><br><span class="line">角色類：player, other_player</span><br></pre></td></tr></table></figure><blockquote><p>💡 先只標記 1~2 個最重要的類別，訓練成功後再擴充。類別越少，模型越好訓練。</p></blockquote><h2 id="💻-步驟三：分割資料集"><a href="#💻-步驟三：分割資料集" class="headerlink" title="💻 步驟三：分割資料集"></a>💻 步驟三：分割資料集</h2><p>將原始截圖與標籤按 8:2 比例隨機分割為訓練集與驗證集，建立對應目錄結構。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># split_dataset.py</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">SRC_IMAGES = <span class="string">"dataset/raw_images"</span></span><br><span class="line">SRC_LABELS = <span class="string">"dataset/raw_labels"</span></span><br><span class="line">DST_ROOT   = <span class="string">"dataset"</span></span><br><span class="line">VAL_RATIO  = <span class="number">0.2</span></span><br><span class="line">random.seed(<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> split <span class="keyword">in</span> [<span class="string">"train"</span>, <span class="string">"val"</span>]:</span><br><span class="line">    os.makedirs(<span class="string">f"<span class="subst">&#123;DST_ROOT&#125;</span>/images/<span class="subst">&#123;split&#125;</span>"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    os.makedirs(<span class="string">f"<span class="subst">&#123;DST_ROOT&#125;</span>/labels/<span class="subst">&#123;split&#125;</span>"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">files = [f <span class="keyword">for</span> f <span class="keyword">in</span> os.listdir(SRC_IMAGES)</span><br><span class="line">         <span class="keyword">if</span> f.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".png"</span>))]</span><br><span class="line">random.shuffle(files)</span><br><span class="line"></span><br><span class="line">val_set = set(files[:int(len(files) * VAL_RATIO)])</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> fname <span class="keyword">in</span> files:</span><br><span class="line">    split = <span class="string">"val"</span> <span class="keyword">if</span> fname <span class="keyword">in</span> val_set <span class="keyword">else</span> <span class="string">"train"</span></span><br><span class="line">    stem  = os.path.splitext(fname)[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    shutil.copy(<span class="string">f"<span class="subst">&#123;SRC_IMAGES&#125;</span>/<span class="subst">&#123;fname&#125;</span>"</span>,</span><br><span class="line">                <span class="string">f"<span class="subst">&#123;DST_ROOT&#125;</span>/images/<span class="subst">&#123;split&#125;</span>/<span class="subst">&#123;fname&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    lbl = <span class="string">f"<span class="subst">&#123;SRC_LABELS&#125;</span>/<span class="subst">&#123;stem&#125;</span>.txt"</span></span><br><span class="line">    <span class="keyword">if</span> os.path.exists(lbl):</span><br><span class="line">        shutil.copy(lbl, <span class="string">f"<span class="subst">&#123;DST_ROOT&#125;</span>/labels/<span class="subst">&#123;split&#125;</span>/<span class="subst">&#123;stem&#125;</span>.txt"</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        open(<span class="string">f"<span class="subst">&#123;DST_ROOT&#125;</span>/labels/<span class="subst">&#123;split&#125;</span>/<span class="subst">&#123;stem&#125;</span>.txt"</span>, <span class="string">"w"</span>).close()</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"train: <span class="subst">&#123;len(files)-len(val_set)&#125;</span>, val: <span class="subst">&#123;len(val_set)&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><h2 id="💻-步驟四：建立-data-yaml"><a href="#💻-步驟四：建立-data-yaml" class="headerlink" title="💻 步驟四：建立 data.yaml"></a>💻 步驟四：建立 data.yaml</h2><p>從 LabelImg 產生的 classes.txt 自動讀取類別，生成 YOLOv8 訓練所需的 data.yaml 設定檔。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># create_yaml.py</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 從 LabelImg 產生的 classes.txt 讀取類別</span></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"dataset/raw_labels/classes.txt"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    classes = [line.strip() <span class="keyword">for</span> line <span class="keyword">in</span> f <span class="keyword">if</span> line.strip()]</span><br><span class="line"></span><br><span class="line">yaml_content = <span class="string">f"""path: ./dataset</span></span><br><span class="line"><span class="string">train: images/train</span></span><br><span class="line"><span class="string">val:   images/val</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">nc: <span class="subst">&#123;len(classes)&#125;</span></span></span><br><span class="line"><span class="string">names:</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">for</span> i, cls <span class="keyword">in</span> enumerate(classes):</span><br><span class="line">    yaml_content += <span class="string">f"  <span class="subst">&#123;i&#125;</span>: <span class="subst">&#123;cls&#125;</span>\n"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"data.yaml"</span>, <span class="string">"w"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    f.write(yaml_content)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"data.yaml 已產生："</span>)</span><br><span class="line">print(yaml_content)</span><br></pre></td></tr></table></figure><h2 id="💻-步驟五：訓練-YOLOv8"><a href="#💻-步驟五：訓練-YOLOv8" class="headerlink" title="💻 步驟五：訓練 YOLOv8"></a>💻 步驟五：訓練 YOLOv8</h2><p>以 YOLOv8s 預訓練模型為基礎，對遊戲截圖資料集進行訓練並輸出最佳偵測模型。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># train.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"GPU：<span class="subst">&#123;<span class="string">'可用 - '</span> + torch.cuda.get_device_name(<span class="number">0</span>) <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">'CPU'</span>&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8s.pt"</span>)</span><br><span class="line"></span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data.yaml"</span>,</span><br><span class="line">    epochs=<span class="number">100</span>,</span><br><span class="line">    imgsz=<span class="number">640</span>,</span><br><span class="line">    batch=<span class="number">16</span>,</span><br><span class="line">    device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,</span><br><span class="line">    workers=<span class="number">0</span>,          <span class="comment"># Windows 必設 0</span></span><br><span class="line">    patience=<span class="number">20</span>,</span><br><span class="line">    name=<span class="string">"lineage_detector"</span>,</span><br><span class="line">    plots=<span class="literal">True</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"\n訓練完成！best.pt：<span class="subst">&#123;model.trainer.best&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><h2 id="💻-步驟六：即時遊戲畫面偵測"><a href="#💻-步驟六：即時遊戲畫面偵測" class="headerlink" title="💻 步驟六：即時遊戲畫面偵測"></a>💻 步驟六：即時遊戲畫面偵測</h2><div class="secret-block" data-key="supporter" data-iter="200000" data-content-iv="EzRHUiumrZ4tgNLC" data-content="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" data-envelopes="W3sic2FsdCI6IjUrcE81bHdZTko2YWpnNG01RkhjMEE9PSIsIml2IjoiMHptblhJODVJVk9VMi9lOSIsIndyYXBwZWQiOiJoMDY2NkNxckhUTXZHS0lMcTBrbFVWdkVLZ1RKZVQvS3VVN3V5ZzhoUnMvQk15NjliYnV3clhXZUVjcVZ2YzVaIn0seyJzYWx0IjoiYTJDM3lFMEJpTHJRdS8vQ2ZNMjFMQT09IiwiaXYiOiJVNFhZemdhdXZlMkZTQ2JWIiwid3JhcHBlZCI6ImRWL045cmQzRTlPWnF0UXJVVlorZjE3djdHU3lZVDhRZ29tWmF2ZkEzclhUVWxMaHJ1SHBBLzFrWkVNNjNTbWEifSx7InNhbHQiOiJ2REoxSXhRaEE3dkwxQzhVbGZLNC9nPT0iLCJpdiI6IkVickhuWkxLTEVBUnNQRlEiLCJ3cmFwcGVkIjoieXJFRUVWRmVtS0xOdEFmK0NTQjlVUUJMdFBQZElmRWFUV2puUEVuTXdTU3hvbU1Kb05ZQ085amNONE9vUnB2dCJ9LHsic2FsdCI6InZSS1FCVkNPZzFlUnU0S293ZUZMdUE9PSIsIml2IjoiaDdKMEQrQjBSUlZUY3UyRCIsIndyYXBwZWQiOiJwbS81YzcxZDFUcHByNGI0czlxM1dpcGl6cDAzczVuMWlqQzNTTUlEL3RGeUxNd2w1ZXFDUW5XMTFOODVHelNVIn1d" data-tier-name="☕ 咖啡會員" style="--tier-color: #16a34a" data-ttl-days="1">  <div class="secret-prompt">    <div class="secret-lock" aria-hidden="true">🔒</div>    <div class="secret-title">這段內容已加密</div>    <div class="secret-hint">需要 <a href="/support" class="secret-tier-link" rel="noopener"><strong class="secret-tier" style="color: #16a34a">☕ 咖啡會員</strong></a> 或更高等級的密碼才能閱讀</div>    <div class="secret-form">      <input type="password" class="secret-input" placeholder="輸入密碼…" autocomplete="off" spellcheck="false">      <button type="button" class="secret-submit">解鎖</button>    </div>    <div class="secret-status" aria-live="polite"></div>    <div class="secret-cta">💡 年度公開密碼，到 <a href="/support">/support</a> 直接取得</div>  </div></div><p><img loading="lazy" src="/images/python/opencv/project-lineage-yolov8/01.png" alt="Python - 圖 2 (01)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-yolov8/02.png" alt="Python - 圖 3 (02)"><br><img loading="lazy" src="/images/python/opencv/project-lineage-yolov8/03.gif" alt="開啟遊戲視窗截圖並以 YOLOv8 即時推論，在單一顯示視窗內用綠色框線標注所有偵測到的物件與信心度"><br><em>圖：開啟遊戲視窗截圖並以 YOLOv8 即時推論，在單一顯示視窗內用綠色框線標注所有偵測到的物件與信心度</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>私服的優勢</strong>：可用 GM 指令隨時生怪、清場、調時間，蒐集成本極低。</li><li><strong>解析度要一致</strong>：訓練與推論的視窗尺寸盡量固定，否則讓 <code>imgsz</code> 涵蓋所有情境。</li><li><strong>標記品質 &gt; 數量</strong>：500 張精標遠勝 2000 張草率標。</li><li><strong>視窗不能最小化</strong>：改用 <code>PrintWindow</code> 後被其他視窗「遮擋」也能擷取，但最小化時 Windows 根本不 render 畫面，仍會拿到黑畫面；程式偵測到最小化會自動結束。</li><li><strong>訓練路徑自動找</strong>：不同 Ultralytics 版本的 <code>best.pt</code> 位置會跑掉，程式已用 <code>rglob</code> 抓最新。</li><li><strong>顯示用手動 cv2，不要 <code>results[0].plot()</code></strong>：部分版本的 <code>plot()</code> 會另開視窗，畫在原 frame 上最可控。</li></ul><h2 id="📊-進階應用方向"><a href="#📊-進階應用方向" class="headerlink" title="📊 進階應用方向"></a>📊 進階應用方向</h2><ul><li><strong>自動攻擊</strong>：偵測到怪物後，取邊界框中心座標，用 pyautogui 點擊</li><li><strong>自動撿取道具</strong>：偵測 <code>item</code> 類別，自動移動到物品位置按撿取</li><li><strong>血量監控</strong>：偵測 HP 條區域，用 OCR 或色彩分析判斷血量百分比</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>從私服蒐集資料、LabelImg 標記、YOLOv8 訓練到即時偵測，整套流程一天內就能跑完；換成其他遊戲或模擬器，步驟完全一樣。</p><p>下一篇進入 <a href="/python-opencv-20260419-python-opencv-project-lineage-assistant"><strong>天堂私服遊戲輔助（一）主程式 GUI 框架與 HP/MP 血量監控</strong></a>，把訓練好的模型整合進一套有 GUI 的完整輔助程式。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​</p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件</a><br><a href="https://github.com/HumanSignal/labelImg" target="_blank" rel="external nofollow noopener noreferrer">LabelImg GitHub</a><br><a href="https://github.com/asweigart/PyGetWindow" target="_blank" rel="external nofollow noopener noreferrer">pygetwindow GitHub</a><br><a href="https://github.com/mhammond/pywin32" target="_blank" rel="external nofollow noopener noreferrer">pywin32 GitHub</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;⚠️-免責聲明&quot;&gt;&lt;a href=&quot;#⚠️-免責聲明&quot; class=&quot;headerlink&quot; title=&quot;⚠️ 免責聲明&quot;&gt;&lt;/a&gt;⚠️ 免責聲明&lt;/h2&gt;&lt;p&gt;本文章內容&lt;strong&gt;僅供學術研究與電腦視覺技術學習之用途&lt;/strong&gt;，所有程式碼與技術說
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：MediaPipe 手勢控制應用</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260417-python-opencv-project-mediapipe-gesture/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260417-python-opencv-project-mediapipe-gesture/</id>
    <published>2026-04-17T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.558Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260416-python-opencv-project-image-classification"><strong>小型圖片分類專案</strong></a> 中，我們完成了端對端的圖片分類系統。</p><p>這一篇介紹 <strong>MediaPipe 手勢控制應用</strong>。MediaPipe 是 Google 開發的跨平台機器學習管線框架，其中的 <strong>Hands 模組</strong>可以即時偵測手部的 21 個關鍵點，不需要 GPU，精度遠高於傳統輪廓法。</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>使用 MediaPipe Hands 偵測手部關鍵點</li><li>實作手指計數（0 ~ 5）</li><li>自訂手勢辨識（OK、Rock、拳頭等）</li><li>整合互動應用：手勢控制螢幕畫筆</li></ul><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install mediapipe</span><br></pre></td></tr></table></figure><h2 id="🔎-MediaPipe-Hands-關鍵點說明"><a href="#🔎-MediaPipe-Hands-關鍵點說明" class="headerlink" title="🔎 MediaPipe Hands 關鍵點說明"></a>🔎 MediaPipe Hands 關鍵點說明</h2><p>MediaPipe Hands 會回傳手部 21 個關鍵點（Landmark），每個點有 <code>x</code>、<code>y</code>（相對比例）與 <code>z</code>（深度）：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​‌​‌‌​​‌​‌​‌‌​​‌​​​‌‌​‌​​‌​‌‌​​​​‌​‌‌‌​​​​​‌‌​‌​​‌​‌‌‌​​​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌‌​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">    8   12  16  20</span><br><span class="line">    |   |   |   |</span><br><span class="line">    7   11  15  19</span><br><span class="line">4   6   10  14  18</span><br><span class="line">|   5   9   13  17</span><br><span class="line">3   |</span><br><span class="line">2   0（手腕）</span><br><span class="line">|</span><br><span class="line">1</span><br></pre></td></tr></table></figure><table><thead><tr><th>關鍵點</th><th>位置</th></tr></thead><tbody><tr><td>0</td><td>手腕</td></tr><tr><td>1~4</td><td>大拇指（4 = 指尖）</td></tr><tr><td>5~8</td><td>食指（8 = 指尖）</td></tr><tr><td>9~12</td><td>中指（12 = 指尖）</td></tr><tr><td>13~16</td><td>無名指（16 = 指尖）</td></tr><tr><td>17~20</td><td>小指（20 = 指尖）</td></tr></tbody></table><h2 id="💻-基本手部偵測"><a href="#💻-基本手部偵測" class="headerlink" title="💻 基本手部偵測"></a>💻 基本手部偵測</h2><p>使用 MediaPipe Hands 偵測手部 21 個關鍵點，即時繪製骨架連線並顯示於攝影機畫面</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># detect_hands.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> mediapipe <span class="keyword">as</span> mp</span><br><span class="line"></span><br><span class="line">mp_hands   = mp.solutions.hands</span><br><span class="line">mp_drawing = mp.solutions.drawing_utils</span><br><span class="line"></span><br><span class="line">hands = mp_hands.Hands(</span><br><span class="line">    static_image_mode=<span class="literal">False</span>,</span><br><span class="line">    max_num_hands=<span class="number">2</span>,</span><br><span class="line">    min_detection_confidence=<span class="number">0.7</span>,</span><br><span class="line">    min_tracking_confidence=<span class="number">0.5</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line">print(<span class="string">"MediaPipe 手部偵測，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    frame = cv2.flip(frame, <span class="number">1</span>)   <span class="comment"># 水平翻轉，讓畫面如鏡子</span></span><br><span class="line">    rgb   = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)</span><br><span class="line">    result = hands.process(rgb)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> result.multi_hand_landmarks:</span><br><span class="line">        <span class="keyword">for</span> hand_lm <span class="keyword">in</span> result.multi_hand_landmarks:</span><br><span class="line">            <span class="comment"># 繪製 21 個關鍵點與連線</span></span><br><span class="line">            mp_drawing.draw_landmarks(</span><br><span class="line">                frame, hand_lm, mp_hands.HAND_CONNECTIONS</span><br><span class="line">            )</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Hands"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><h2 id="💻-手指計數"><a href="#💻-手指計數" class="headerlink" title="💻 手指計數"></a>💻 手指計數</h2><p>比較各手指指尖與第二關節的座標判斷每根手指是否伸直，回傳伸出的手指數量</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># demo_count_fingers.py</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">count_fingers</span><span class="params">(hand_lm, handedness)</span>:</span></span><br><span class="line">    <span class="string">"""</span></span><br><span class="line"><span class="string">    回傳伸出的手指數量（0~5）</span></span><br><span class="line"><span class="string">    handedness: "Left" 或 "Right"（MediaPipe 回傳的是鏡像後的手）</span></span><br><span class="line"><span class="string">    """</span></span><br><span class="line">    lm = hand_lm.landmark</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 指尖與第二關節的 ID</span></span><br><span class="line">    tip_ids  = [<span class="number">4</span>, <span class="number">8</span>, <span class="number">12</span>, <span class="number">16</span>, <span class="number">20</span>]</span><br><span class="line">    pip_ids  = [<span class="number">3</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">14</span>, <span class="number">18</span>]</span><br><span class="line"></span><br><span class="line">    fingers = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 大拇指：比較 x 軸（左右手方向不同）</span></span><br><span class="line">    <span class="keyword">if</span> handedness == <span class="string">"Right"</span>:</span><br><span class="line">        <span class="keyword">if</span> lm[tip_ids[<span class="number">0</span>]].x &lt; lm[pip_ids[<span class="number">0</span>]].x:</span><br><span class="line">            fingers += <span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">if</span> lm[tip_ids[<span class="number">0</span>]].x &gt; lm[pip_ids[<span class="number">0</span>]].x:</span><br><span class="line">            fingers += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 其他四指：比較 y 軸（指尖 y &lt; 第二關節 y 表示伸直）</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">1</span>, <span class="number">5</span>):</span><br><span class="line">        <span class="keyword">if</span> lm[tip_ids[i]].y &lt; lm[pip_ids[i]].y:</span><br><span class="line">            fingers += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> fingers</span><br></pre></td></tr></table></figure><h2 id="💻-手勢辨識"><a href="#💻-手勢辨識" class="headerlink" title="💻 手勢辨識"></a>💻 手勢辨識</h2><p>根據關鍵點幾何關係辨識拳頭、讚、剪刀、OK、五指張開等特定手勢並回傳對應名稱​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​‌​‌‌​​‌​‌​‌‌​​‌​​​‌‌​‌​​‌​‌‌​​​​‌​‌‌‌​​​​​‌‌​‌​​‌​‌‌‌​​​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌‌​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># demo_recognize_gesture.py</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">recognize_gesture</span><span class="params">(hand_lm)</span>:</span></span><br><span class="line">    <span class="string">"""辨識特定手勢，回傳手勢名稱"""</span></span><br><span class="line">    lm = hand_lm.landmark</span><br><span class="line"></span><br><span class="line">    tip_ids = [<span class="number">4</span>, <span class="number">8</span>, <span class="number">12</span>, <span class="number">16</span>, <span class="number">20</span>]</span><br><span class="line">    pip_ids = [<span class="number">3</span>, <span class="number">6</span>, <span class="number">10</span>, <span class="number">14</span>, <span class="number">18</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 各手指是否伸直</span></span><br><span class="line">    fingers = [lm[tip_ids[i]].y &lt; lm[pip_ids[i]].y <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">1</span>, <span class="number">5</span>)]</span><br><span class="line">    <span class="comment"># 大拇指（簡化：只看是否向側邊伸出）</span></span><br><span class="line">    thumb_open = abs(lm[<span class="number">4</span>].x - lm[<span class="number">3</span>].x) &gt; <span class="number">0.04</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 拳頭：所有手指收起</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> any(fingers) <span class="keyword">and</span> <span class="keyword">not</span> thumb_open:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"✊ 拳頭"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 讚：只有大拇指伸出，其他收起</span></span><br><span class="line">    <span class="keyword">if</span> thumb_open <span class="keyword">and</span> <span class="keyword">not</span> any(fingers):</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"👍 讚"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 剪刀：食指與中指伸出</span></span><br><span class="line">    <span class="keyword">if</span> fingers[<span class="number">0</span>] <span class="keyword">and</span> fingers[<span class="number">1</span>] <span class="keyword">and</span> <span class="keyword">not</span> fingers[<span class="number">2</span>] <span class="keyword">and</span> <span class="keyword">not</span> fingers[<span class="number">3</span>]:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"✌️ 剪刀"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># OK：食指與大拇指靠近（其他手指伸出）</span></span><br><span class="line">    dist = ((lm[<span class="number">4</span>].x - lm[<span class="number">8</span>].x)**<span class="number">2</span> + (lm[<span class="number">4</span>].y - lm[<span class="number">8</span>].y)**<span class="number">2</span>) ** <span class="number">0.5</span></span><br><span class="line">    <span class="keyword">if</span> dist &lt; <span class="number">0.06</span> <span class="keyword">and</span> fingers[<span class="number">1</span>] <span class="keyword">and</span> fingers[<span class="number">2</span>] <span class="keyword">and</span> fingers[<span class="number">3</span>]:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"👌 OK"</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 五指全開</span></span><br><span class="line">    <span class="keyword">if</span> all(fingers) <span class="keyword">and</span> thumb_open:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"🖐️ 五指張開"</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="string">""</span></span><br></pre></td></tr></table></figure><h2 id="💻-完整互動應用：手勢畫筆"><a href="#💻-完整互動應用：手勢畫筆" class="headerlink" title="💻 完整互動應用：手勢畫筆"></a>💻 完整互動應用：手勢畫筆</h2><p>伸出食指時在畫布上繪製軌跡，握拳時清除畫布，將手勢繪圖疊加在攝影機畫面上顯示</p><p>用手勢控制螢幕畫筆，食指繪圖、拳頭清除畫布：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># gesture_painter.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> mediapipe <span class="keyword">as</span> mp</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">mp_hands   = mp.solutions.hands</span><br><span class="line">mp_drawing = mp.solutions.drawing_utils</span><br><span class="line"></span><br><span class="line">hands  = mp_hands.Hands(max_num_hands=<span class="number">1</span>,</span><br><span class="line">                         min_detection_confidence=<span class="number">0.7</span>,</span><br><span class="line">                         min_tracking_confidence=<span class="number">0.5</span>)</span><br><span class="line">cap    = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line">canvas = <span class="literal">None</span></span><br><span class="line">prev_x, prev_y = <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line">color  = (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"食指繪圖，拳頭清除，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    frame  = cv2.flip(frame, <span class="number">1</span>)</span><br><span class="line">    h, w   = frame.shape[:<span class="number">2</span>]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> canvas <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        canvas = np.zeros_like(frame)</span><br><span class="line"></span><br><span class="line">    rgb    = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)</span><br><span class="line">    result = hands.process(rgb)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> result.multi_hand_landmarks:</span><br><span class="line">        hand_lm = result.multi_hand_landmarks[<span class="number">0</span>]</span><br><span class="line">        lm      = hand_lm.landmark</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 食指指尖位置</span></span><br><span class="line">        ix = int(lm[<span class="number">8</span>].x * w)</span><br><span class="line">        iy = int(lm[<span class="number">8</span>].y * h)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 手指計數判斷手勢</span></span><br><span class="line">        tip_ids = [<span class="number">8</span>, <span class="number">12</span>, <span class="number">16</span>, <span class="number">20</span>]</span><br><span class="line">        pip_ids = [<span class="number">6</span>, <span class="number">10</span>, <span class="number">14</span>, <span class="number">18</span>]</span><br><span class="line">        finger_count = sum(</span><br><span class="line">            lm[tip_ids[i]].y &lt; lm[pip_ids[i]].y <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">4</span>)</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> finger_count == <span class="number">1</span>:</span><br><span class="line">            <span class="comment"># 只伸出食指：繪圖模式</span></span><br><span class="line">            <span class="keyword">if</span> prev_x <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                cv2.line(canvas, (prev_x, prev_y), (ix, iy), color, <span class="number">5</span>)</span><br><span class="line">            prev_x, prev_y = ix, iy</span><br><span class="line">        <span class="keyword">elif</span> finger_count == <span class="number">0</span>:</span><br><span class="line">            <span class="comment"># 拳頭：清除畫布</span></span><br><span class="line">            canvas = np.zeros_like(frame)</span><br><span class="line">            prev_x, prev_y = <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            prev_x, prev_y = <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">        mp_drawing.draw_landmarks(frame, hand_lm, mp_hands.HAND_CONNECTIONS)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 疊加畫布</span></span><br><span class="line">    output = cv2.addWeighted(frame, <span class="number">0.7</span>, canvas, <span class="number">0.3</span>, <span class="number">0</span>)</span><br><span class="line">    cv2.putText(output, <span class="string">"食指:畫  拳頭:清除  q:離開"</span>, (<span class="number">10</span>, <span class="number">30</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.7</span>, (<span class="number">255</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Gesture Painter"</span>, output)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong><code>cv2.flip(frame, 1)</code> 水平翻轉很重要</strong>：不翻轉的話，MediaPipe 回傳的 Left/Right 與畫面方向相反，大拇指方向判斷會錯誤。</li><li><strong>MediaPipe 的 <code>handedness</code> 是鏡像判斷</strong>：對著攝影機時，MediaPipe 判斷的「右手」實際上是你的左手（因為是鏡像）。翻轉畫面後就正確了。</li><li><strong><code>z</code> 值可用於判斷手指深度</strong>：若需要更精細的手勢（例如區分手掌朝前/朝後），可利用 <code>z</code> 值輔助判斷。</li><li><strong>在黑暗環境效果較差</strong>：MediaPipe Hands 依賴 RGB 畫面，光線不足時偵測率會下降。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>MediaPipe Hands 提供了高精度、低延遲的手部關鍵點偵測，讓複雜的手勢辨識只需要幾何計算就能完成，不需要另外訓練模型。<br>下一個專案是 <a href="/python-opencv-20260418-python-opencv-project-lineage-yolov8"><strong>天堂私服 YOLOv8 物件偵測實戰</strong></a>，把 YOLOv8 訓練技術應用在遊戲場景中。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​‌​‌‌​​‌​‌​‌‌​​‌​​​‌‌​‌​​‌​‌‌​​​​‌​‌‌‌​​​​​‌‌​‌​​‌​‌‌‌​​​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​‌‌‌​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌‌​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker" target="_blank" rel="external nofollow noopener noreferrer">MediaPipe Hands 官方文件</a><br><a href="https://google.github.io/mediapipe/solutions/hands.html" target="_blank" rel="external nofollow noopener noreferrer">MediaPipe Python Solutions</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260416-python-opencv-projec
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：小型圖片分類專案</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260416-python-opencv-project-image-classification/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260416-python-opencv-project-image-classification/</id>
    <published>2026-04-16T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.558Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260415-python-opencv-project-license-plate"><strong>車牌辨識應用</strong></a> 中，我們完成了結合 OCR 的辨識應用。</p><p>這一篇是 <strong>小型圖片分類專案</strong>，目標是把 <a href="/python-opencv-20260323-python-opencv-model-training">模型訓練與微調</a> 系列學到的遷移學習技術，整合成一個完整的端對端系統：<strong>從蒐集資料到即時攝影機推論，一次走完全流程</strong>。</p><p>範例場景：辨識「剪刀、石頭、布」三種手勢（可替換成任何你想分類的物件）。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌​​‌‌​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>用攝影機蒐集自訂類別的訓練資料</li><li>以 MobileNetV2 + 遷移學習訓練分類模型</li><li>評估模型表現</li><li>整合攝影機做即時分類推論</li></ul><h2 id="🗃️-專案結構"><a href="#🗃️-專案結構" class="headerlink" title="🗃️ 專案結構"></a>🗃️ 專案結構</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">image_classifier&#x2F;</span><br><span class="line">├── 1_collect.py       ← 蒐集資料</span><br><span class="line">├── 2_train.py         ← 訓練模型</span><br><span class="line">├── 3_evaluate.py      ← 評估模型</span><br><span class="line">├── 4_inference.py     ← 即時推論</span><br><span class="line">├── dataset&#x2F;           ← 訓練資料（自動建立）</span><br><span class="line">│   ├── train&#x2F;</span><br><span class="line">│   │   ├── scissors&#x2F;</span><br><span class="line">│   │   ├── rock&#x2F;</span><br><span class="line">│   │   └── paper&#x2F;</span><br><span class="line">│   └── val&#x2F;</span><br><span class="line">│       ├── scissors&#x2F;</span><br><span class="line">│       ├── rock&#x2F;</span><br><span class="line">│       └── paper&#x2F;</span><br><span class="line">└── models&#x2F;</span><br><span class="line">    └── classifier.pth</span><br></pre></td></tr></table></figure><h2 id="💻-步驟一：蒐集訓練資料"><a href="#💻-步驟一：蒐集訓練資料" class="headerlink" title="💻 步驟一：蒐集訓練資料"></a>💻 步驟一：蒐集訓練資料</h2><p>開啟攝影機讓使用者逐類別按空白鍵蒐集訓練與驗證圖片，截取中央 ROI 後儲存。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1_collect.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">CLASSES    = [<span class="string">"scissors"</span>, <span class="string">"rock"</span>, <span class="string">"paper"</span>]   <span class="comment"># 修改成你的類別</span></span><br><span class="line">TRAIN_COUNT = <span class="number">200</span>    <span class="comment"># 每類訓練樣本數</span></span><br><span class="line">VAL_COUNT   = <span class="number">50</span>     <span class="comment"># 每類驗證樣本數</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> cls <span class="keyword">in</span> CLASSES:</span><br><span class="line">    os.makedirs(<span class="string">f"dataset/train/<span class="subst">&#123;cls&#125;</span>"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    os.makedirs(<span class="string">f"dataset/val/<span class="subst">&#123;cls&#125;</span>"</span>,   exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> cls <span class="keyword">in</span> CLASSES:</span><br><span class="line">    <span class="keyword">for</span> split, target <span class="keyword">in</span> [(<span class="string">"train"</span>, TRAIN_COUNT), (<span class="string">"val"</span>, VAL_COUNT)]:</span><br><span class="line">        count = <span class="number">0</span></span><br><span class="line">        print(<span class="string">f"\n準備蒐集 [<span class="subst">&#123;cls&#125;</span>] <span class="subst">&#123;split&#125;</span> 資料，按空白鍵開始"</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 等待使用者準備好</span></span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            ret, frame = cap.read()</span><br><span class="line">            cv2.putText(frame, <span class="string">f"準備好後按空白鍵：<span class="subst">&#123;cls&#125;</span> (<span class="subst">&#123;split&#125;</span>)"</span>,</span><br><span class="line">                        (<span class="number">10</span>, <span class="number">40</span>), cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.8</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="number">2</span>)</span><br><span class="line">            cv2.imshow(<span class="string">"Collect"</span>, frame)</span><br><span class="line">            <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">" "</span>):</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 開始蒐集</span></span><br><span class="line">        <span class="keyword">while</span> count &lt; target:</span><br><span class="line">            ret, frame = cap.read()</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 截取中央 ROI 作為訓練圖片</span></span><br><span class="line">            h, w  = frame.shape[:<span class="number">2</span>]</span><br><span class="line">            size  = min(h, w) // <span class="number">2</span></span><br><span class="line">            cx, cy = w // <span class="number">2</span>, h // <span class="number">2</span></span><br><span class="line">            roi   = frame[cy-size//<span class="number">2</span>:cy+size//<span class="number">2</span>, cx-size//<span class="number">2</span>:cx+size//<span class="number">2</span>]</span><br><span class="line">            roi_rsz = cv2.resize(roi, (<span class="number">224</span>, <span class="number">224</span>))</span><br><span class="line"></span><br><span class="line">            fname = os.path.join(<span class="string">f"dataset/<span class="subst">&#123;split&#125;</span>/<span class="subst">&#123;cls&#125;</span>"</span>, <span class="string">f"<span class="subst">&#123;count:<span class="number">04</span>d&#125;</span>.jpg"</span>)</span><br><span class="line">            cv2.imwrite(fname, roi_rsz)</span><br><span class="line">            count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            cv2.rectangle(frame, (cx-size//<span class="number">2</span>, cy-size//<span class="number">2</span>),</span><br><span class="line">                          (cx+size//<span class="number">2</span>, cy+size//<span class="number">2</span>), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">            cv2.putText(frame, <span class="string">f"<span class="subst">&#123;cls&#125;</span> <span class="subst">&#123;split&#125;</span>: <span class="subst">&#123;count&#125;</span>/<span class="subst">&#123;target&#125;</span>"</span>,</span><br><span class="line">                        (<span class="number">10</span>, <span class="number">40</span>), cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.8</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">            cv2.imshow(<span class="string">"Collect"</span>, frame)</span><br><span class="line">            cv2.waitKey(<span class="number">30</span>)   <span class="comment"># 控制蒐集速度</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br><span class="line">print(<span class="string">"資料蒐集完成！"</span>)</span><br></pre></td></tr></table></figure><h2 id="💻-步驟二：訓練模型"><a href="#💻-步驟二：訓練模型" class="headerlink" title="💻 步驟二：訓練模型"></a>💻 步驟二：訓練模型</h2><p>凍結 MobileNetV2 特徵層後只訓練分類頭，帶有增強的訓練迴圈並儲存最佳驗證準確率的模型</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 2_train.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> datasets, transforms, models</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">CLASSES  = [<span class="string">"scissors"</span>, <span class="string">"rock"</span>, <span class="string">"paper"</span>]</span><br><span class="line">EPOCHS   = <span class="number">20</span></span><br><span class="line">BATCH    = <span class="number">32</span></span><br><span class="line">LR       = <span class="number">1e-4</span></span><br><span class="line">DEVICE   = torch.device(<span class="string">"cuda"</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"使用裝置：<span class="subst">&#123;DEVICE&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 資料增強</span></span><br><span class="line">train_tf = transforms.Compose([</span><br><span class="line">    transforms.RandomHorizontalFlip(),</span><br><span class="line">    transforms.RandomRotation(<span class="number">15</span>),</span><br><span class="line">    transforms.ColorJitter(brightness=<span class="number">0.3</span>, contrast=<span class="number">0.3</span>),</span><br><span class="line">    transforms.ToTensor(),</span><br><span class="line">    transforms.Normalize([<span class="number">0.485</span>, <span class="number">0.456</span>, <span class="number">0.406</span>],</span><br><span class="line">                         [<span class="number">0.229</span>, <span class="number">0.224</span>, <span class="number">0.225</span>]),</span><br><span class="line">])</span><br><span class="line">val_tf = transforms.Compose([</span><br><span class="line">    transforms.ToTensor(),</span><br><span class="line">    transforms.Normalize([<span class="number">0.485</span>, <span class="number">0.456</span>, <span class="number">0.406</span>],</span><br><span class="line">                         [<span class="number">0.229</span>, <span class="number">0.224</span>, <span class="number">0.225</span>]),</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">train_ds = datasets.ImageFolder(<span class="string">"dataset/train"</span>, transform=train_tf)</span><br><span class="line">val_ds   = datasets.ImageFolder(<span class="string">"dataset/val"</span>,   transform=val_tf)</span><br><span class="line">train_dl = DataLoader(train_ds, batch_size=BATCH, shuffle=<span class="literal">True</span>)</span><br><span class="line">val_dl   = DataLoader(val_ds,   batch_size=BATCH)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"訓練集：<span class="subst">&#123;len(train_ds)&#125;</span> 張，驗證集：<span class="subst">&#123;len(val_ds)&#125;</span> 張"</span>)</span><br><span class="line">print(<span class="string">f"類別對應：<span class="subst">&#123;train_ds.class_to_idx&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># MobileNetV2 遷移學習</span></span><br><span class="line">model = models.mobilenet_v2(weights=models.MobileNet_V2_Weights.DEFAULT)</span><br><span class="line"><span class="keyword">for</span> param <span class="keyword">in</span> model.features.parameters():</span><br><span class="line">    param.requires_grad = <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">model.classifier = nn.Sequential(</span><br><span class="line">    nn.Dropout(<span class="number">0.2</span>),</span><br><span class="line">    nn.Linear(model.last_channel, len(CLASSES)),</span><br><span class="line">)</span><br><span class="line">model = model.to(DEVICE)</span><br><span class="line"></span><br><span class="line">criterion = nn.CrossEntropyLoss()</span><br><span class="line">optimizer = torch.optim.Adam(model.classifier.parameters(), lr=LR)</span><br><span class="line"></span><br><span class="line">best_acc = <span class="number">0.0</span></span><br><span class="line">os.makedirs(<span class="string">"models"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">1</span>, EPOCHS + <span class="number">1</span>):</span><br><span class="line">    <span class="comment"># --- 訓練 ---</span></span><br><span class="line">    model.train()</span><br><span class="line">    total, correct = <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">    <span class="keyword">for</span> imgs, labels <span class="keyword">in</span> train_dl:</span><br><span class="line">        imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)</span><br><span class="line">        optimizer.zero_grad()</span><br><span class="line">        out  = model(imgs)</span><br><span class="line">        loss = criterion(out, labels)</span><br><span class="line">        loss.backward()</span><br><span class="line">        optimizer.step()</span><br><span class="line">        correct += (out.argmax(<span class="number">1</span>) == labels).sum().item()</span><br><span class="line">        total   += labels.size(<span class="number">0</span>)</span><br><span class="line">    train_acc = correct / total</span><br><span class="line"></span><br><span class="line">    <span class="comment"># --- 驗證 ---</span></span><br><span class="line">    model.eval()</span><br><span class="line">    total, correct = <span class="number">0</span>, <span class="number">0</span></span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        <span class="keyword">for</span> imgs, labels <span class="keyword">in</span> val_dl:</span><br><span class="line">            imgs, labels = imgs.to(DEVICE), labels.to(DEVICE)</span><br><span class="line">            out      = model(imgs)</span><br><span class="line">            correct += (out.argmax(<span class="number">1</span>) == labels).sum().item()</span><br><span class="line">            total   += labels.size(<span class="number">0</span>)</span><br><span class="line">    val_acc = correct / total</span><br><span class="line"></span><br><span class="line">    print(<span class="string">f"Epoch <span class="subst">&#123;epoch:<span class="number">02</span>d&#125;</span>/<span class="subst">&#123;EPOCHS&#125;</span>  "</span></span><br><span class="line">          <span class="string">f"train_acc=<span class="subst">&#123;train_acc:<span class="number">.4</span>f&#125;</span>  val_acc=<span class="subst">&#123;val_acc:<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> val_acc &gt; best_acc:</span><br><span class="line">        best_acc = val_acc</span><br><span class="line">        torch.save(model.state_dict(), <span class="string">"models/classifier.pth"</span>)</span><br><span class="line">        print(<span class="string">f"  ✅ 最佳模型已儲存（val_acc=<span class="subst">&#123;val_acc:<span class="number">.4</span>f&#125;</span>）"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"\n訓練完成！最佳驗證準確率：<span class="subst">&#123;best_acc:<span class="number">.4</span>f&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><h2 id="💻-步驟三：評估模型"><a href="#💻-步驟三：評估模型" class="headerlink" title="💻 步驟三：評估模型"></a>💻 步驟三：評估模型</h2><p>載入已訓練的 MobileNetV2 模型對驗證集推論，輸出各類別 Precision、Recall、F1-Score 報告​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌​​‌‌​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 3_evaluate.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> datasets, transforms, models</span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> classification_report</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">CLASSES = [<span class="string">"scissors"</span>, <span class="string">"rock"</span>, <span class="string">"paper"</span>]</span><br><span class="line">DEVICE  = torch.device(<span class="string">"cuda"</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>)</span><br><span class="line"></span><br><span class="line">val_tf = transforms.Compose([</span><br><span class="line">    transforms.ToTensor(),</span><br><span class="line">    transforms.Normalize([<span class="number">0.485</span>, <span class="number">0.456</span>, <span class="number">0.406</span>],</span><br><span class="line">                         [<span class="number">0.229</span>, <span class="number">0.224</span>, <span class="number">0.225</span>]),</span><br><span class="line">])</span><br><span class="line">val_ds = datasets.ImageFolder(<span class="string">"dataset/val"</span>, transform=val_tf)</span><br><span class="line">val_dl = DataLoader(val_ds, batch_size=<span class="number">32</span>)</span><br><span class="line"></span><br><span class="line">model = models.mobilenet_v2(weights=<span class="literal">None</span>)</span><br><span class="line">model.classifier = nn.Sequential(</span><br><span class="line">    nn.Dropout(<span class="number">0.2</span>),</span><br><span class="line">    nn.Linear(model.last_channel, len(CLASSES)),</span><br><span class="line">)</span><br><span class="line">model.load_state_dict(torch.load(<span class="string">"models/classifier.pth"</span>, map_location=DEVICE))</span><br><span class="line">model = model.to(DEVICE)</span><br><span class="line">model.eval()</span><br><span class="line"></span><br><span class="line">all_preds, all_labels = [], []</span><br><span class="line"><span class="keyword">with</span> torch.no_grad():</span><br><span class="line">    <span class="keyword">for</span> imgs, labels <span class="keyword">in</span> val_dl:</span><br><span class="line">        imgs   = imgs.to(DEVICE)</span><br><span class="line">        preds  = model(imgs).argmax(<span class="number">1</span>).cpu().numpy()</span><br><span class="line">        all_preds.extend(preds)</span><br><span class="line">        all_labels.extend(labels.numpy())</span><br><span class="line"></span><br><span class="line">print(classification_report(all_labels, all_preds, target_names=CLASSES))</span><br></pre></td></tr></table></figure><h2 id="💻-步驟四：整合攝影機即時推論"><a href="#💻-步驟四：整合攝影機即時推論" class="headerlink" title="💻 步驟四：整合攝影機即時推論"></a>💻 步驟四：整合攝影機即時推論</h2><p>整合 MobileNetV2 與攝影機，對畫面中央 ROI 即時分類手勢並顯示類別名稱與信心度</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 4_inference.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> transforms, models</span><br><span class="line"></span><br><span class="line">CLASSES = [<span class="string">"scissors"</span>, <span class="string">"rock"</span>, <span class="string">"paper"</span>]</span><br><span class="line">DEVICE  = torch.device(<span class="string">"cuda"</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>)</span><br><span class="line"></span><br><span class="line">model = models.mobilenet_v2(weights=<span class="literal">None</span>)</span><br><span class="line">model.classifier = nn.Sequential(</span><br><span class="line">    nn.Dropout(<span class="number">0.2</span>),</span><br><span class="line">    nn.Linear(model.last_channel, len(CLASSES)),</span><br><span class="line">)</span><br><span class="line">model.load_state_dict(torch.load(<span class="string">"models/classifier.pth"</span>, map_location=DEVICE))</span><br><span class="line">model = model.to(DEVICE)</span><br><span class="line">model.eval()</span><br><span class="line"></span><br><span class="line">tf = transforms.Compose([</span><br><span class="line">    transforms.ToTensor(),</span><br><span class="line">    transforms.Normalize([<span class="number">0.485</span>, <span class="number">0.456</span>, <span class="number">0.406</span>],</span><br><span class="line">                         [<span class="number">0.229</span>, <span class="number">0.224</span>, <span class="number">0.225</span>]),</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line">print(<span class="string">"即時分類推論，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    h, w   = frame.shape[:<span class="number">2</span>]</span><br><span class="line">    size   = min(h, w) // <span class="number">2</span></span><br><span class="line">    cx, cy = w // <span class="number">2</span>, h // <span class="number">2</span></span><br><span class="line">    roi    = frame[cy-size//<span class="number">2</span>:cy+size//<span class="number">2</span>, cx-size//<span class="number">2</span>:cx+size//<span class="number">2</span>]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 前處理</span></span><br><span class="line">    roi_rgb = cv2.cvtColor(cv2.resize(roi, (<span class="number">224</span>, <span class="number">224</span>)), cv2.COLOR_BGR2RGB)</span><br><span class="line">    <span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line">    pil_img = Image.fromarray(roi_rgb)</span><br><span class="line">    tensor  = tf(pil_img).unsqueeze(<span class="number">0</span>).to(DEVICE)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> torch.no_grad():</span><br><span class="line">        logits = model(tensor)</span><br><span class="line">        probs  = torch.softmax(logits, dim=<span class="number">1</span>)[<span class="number">0</span>].cpu().numpy()</span><br><span class="line">        pred   = probs.argmax()</span><br><span class="line"></span><br><span class="line">    label = <span class="string">f"<span class="subst">&#123;CLASSES[pred]&#125;</span>: <span class="subst">&#123;probs[pred]*<span class="number">100</span>:<span class="number">.1</span>f&#125;</span>%"</span></span><br><span class="line">    cv2.rectangle(frame, (cx-size//<span class="number">2</span>, cy-size//<span class="number">2</span>),</span><br><span class="line">                  (cx+size//<span class="number">2</span>, cy+size//<span class="number">2</span>), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">    cv2.putText(frame, label, (<span class="number">10</span>, <span class="number">40</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">1.2</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Classifier"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>蒐集資料時保持背景一致</strong>：背景差異太大會讓模型學到背景而非物件本身，建議在相同背景下蒐集。</li><li><strong>類別樣本數要均衡</strong>：每個類別樣本數差異不要超過 2 倍，否則模型會偏向樣本多的類別。</li><li><strong>推論 ROI 與訓練 ROI 要一致</strong>：訓練時取畫面中央 ROI，推論時也要取相同位置，避免分佈偏移。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>這個專案把資料蒐集、模型訓練、評估、即時推論串成一條完整的 pipeline，是深度學習與 OpenCV 整合最典型的範例。<br>下一個專案是 <a href="/python-opencv-20260417-python-opencv-project-mediapipe-gesture"><strong>MediaPipe 手勢控制應用</strong></a>，用 MediaPipe 的手部關鍵點做更精細的手勢辨識。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌‌​​‌‌​‌‌​‌​​‌​‌‌​​‌‌​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><blockquote><p>註：以上參考了<br><a href="https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html" target="_blank" rel="external nofollow noopener noreferrer">PyTorch Transfer Learning Tutorial</a><br><a href="https://pytorch.org/vision/stable/models/mobilenetv2.html" target="_blank" rel="external nofollow noopener noreferrer">torchvision MobileNetV2</a><br><a href="https://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html" target="_blank" rel="external nofollow noopener noreferrer">scikit-learn classification_report</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260415-python-opencv-projec
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：車牌辨識應用</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260415-python-opencv-project-license-plate/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260415-python-opencv-project-license-plate/</id>
    <published>2026-04-15T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260414-python-opencv-project-face-recognition"><strong>即時人臉辨識系統</strong></a> 中，我們建立了一個完整的人臉辨識 pipeline。</p><p>這一篇要挑戰另一個經典應用：<strong>車牌辨識（License Plate Recognition，LPR）</strong>。<br>車牌辨識結合了邊緣偵測、輪廓分析、透視矯正與 OCR 文字辨識，是一個整合前面多個章節知識的綜合專案。</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>從圖片或影片畫面中定位車牌區域</li><li>對車牌區域進行透視矯正與前處理</li><li>使用 EasyOCR 辨識車牌號碼</li><li>以影片檔或 MJPEG 串流掃描，顯示即時辨識結果</li></ul><h2 id="🎨-範例圖片"><a href="#🎨-範例圖片" class="headerlink" title="🎨 範例圖片"></a>🎨 範例圖片</h2><ul><li>來源：<a href="https://www.pexels.com/zh-tw/photo/19036038/" target="_blank" rel="external nofollow noopener noreferrer">Pexels - License Plate Image</a>，屬於無版權圖片，可自由下載與使用。</li><li>內容：圖片車頭且有拍攝到車牌，非常適合用來做車牌辨識測試。</li><li>下載後將檔名改為 <code>license_plate.jpg</code>，放到專案的 <code>assets/</code> 目錄下。</li></ul><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><p><strong>步驟 1：安裝 EasyOCR</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install easyocr</span><br></pre></td></tr></table></figure><p><strong>步驟 2：修復 OpenCV（必做）</strong></p><p><code>pip install easyocr</code> 會連帶安裝 <code>opencv-python-headless</code>（無 GUI 版本），覆蓋原先的 <code>opencv-python</code> / <code>opencv-contrib-python</code>，造成下列兩種常見錯誤：</p><ul><li><code>cv2.imshow</code> → <code>The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support.</code></li><li><code>cv2.imread</code> → <code>AttributeError: module &#39;cv2&#39; has no attribute &#39;imread&#39;</code>（多個 opencv 變體並存時出現）</li></ul><p>最穩定的做法是<strong>完整清除所有 opencv 變體後再乾淨安裝</strong>：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 清除所有 opencv 變體（無論是否存在都執行一次）</span></span><br><span class="line">pip uninstall -y opencv-python opencv-python-headless opencv-contrib-python opencv-contrib-python-headless</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 確認已無殘留，以下指令應該沒有任何輸出</span></span><br><span class="line">pip list | findstr opencv     <span class="comment"># Windows</span></span><br><span class="line"><span class="comment"># pip list | grep opencv      # macOS / Linux</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 乾淨安裝 contrib 版（--no-cache-dir 避免使用損壞快取）</span></span><br><span class="line">pip install --no-cache-dir opencv-contrib-python</span><br></pre></td></tr></table></figure><p><strong>步驟 3：驗證安裝</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line">print(cv2.__file__)           <span class="comment"># 路徑裡不應該出現 "headless"</span></span><br><span class="line">print(cv2.__version__)</span><br><span class="line">print(<span class="string">"imread"</span> <span class="keyword">in</span> dir(cv2))   <span class="comment"># 應為 True</span></span><br></pre></td></tr></table></figure><blockquote><p>💡 EasyOCR 首次執行時會自動下載語言模型（繁體中文 + 英文約 200MB），需要網路連線。</p></blockquote><blockquote><p>💡 執行時若看到 <code>Using CPU. Note: This module is much faster with a GPU.</code>，這是 <code>gpu=False</code> 的正常提示訊息，不是錯誤；本篇示範以 CPU 執行即可，若機器有 CUDA GPU 可將 <code>easyocr.Reader([&quot;en&quot;], gpu=True)</code> 以加速推論。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p></blockquote><blockquote><p>💡 若 <code>pip list</code> 出現 <code>WARNING: Ignoring invalid distribution -xxx</code>，代表 site-packages 內有 <code>~xxx</code> 開頭的殘留資料夾（先前安裝被中斷留下），不影響本次安裝；可執行 <code>Remove-Item -Recurse -Force &quot;&lt;venv 路徑&gt;\Lib\site-packages\~*&quot;</code> 清除。</p></blockquote><h2 id="💻-步驟一：從靜態圖片辨識車牌"><a href="#💻-步驟一：從靜態圖片辨識車牌" class="headerlink" title="💻 步驟一：從靜態圖片辨識車牌"></a>💻 步驟一：從靜態圖片辨識車牌</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># recognize_plate.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> easyocr</span><br><span class="line"></span><br><span class="line">reader = easyocr.Reader([<span class="string">"en"</span>], gpu=<span class="literal">False</span>)   <span class="comment"># 台灣車牌為英數字，使用英文模型即可</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find_plate_candidates</span><span class="params">(img)</span>:</span></span><br><span class="line">    <span class="string">"""找出可能是車牌的矩形輪廓"""</span></span><br><span class="line">    gray    = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    blur    = cv2.bilateralFilter(gray, <span class="number">11</span>, <span class="number">17</span>, <span class="number">17</span>)</span><br><span class="line">    edges   = cv2.Canny(blur, <span class="number">30</span>, <span class="number">200</span>)</span><br><span class="line"></span><br><span class="line">    contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)</span><br><span class="line">    contours     = sorted(contours, key=cv2.contourArea, reverse=<span class="literal">True</span>)[:<span class="number">20</span>]</span><br><span class="line"></span><br><span class="line">    candidates = []</span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> contours:</span><br><span class="line">        peri   = cv2.arcLength(c, <span class="literal">True</span>)</span><br><span class="line">        approx = cv2.approxPolyDP(c, <span class="number">0.018</span> * peri, <span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">if</span> len(approx) == <span class="number">4</span>:</span><br><span class="line">            candidates.append(approx)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> candidates</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">crop_plate</span><span class="params">(img, contour)</span>:</span></span><br><span class="line">    <span class="string">"""根據輪廓裁切車牌區域"""</span></span><br><span class="line">    x, y, w, h = cv2.boundingRect(contour)</span><br><span class="line">    <span class="keyword">return</span> img[y:y+h, x:x+w], (x, y, w, h)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">preprocess_plate</span><span class="params">(plate_img)</span>:</span></span><br><span class="line">    <span class="string">"""車牌影像前處理"""</span></span><br><span class="line">    gray    = cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    resized = cv2.resize(gray, (<span class="number">300</span>, <span class="number">80</span>))</span><br><span class="line">    _, binary = cv2.threshold(resized, <span class="number">0</span>, <span class="number">255</span>,</span><br><span class="line">                              cv2.THRESH_BINARY + cv2.THRESH_OTSU)</span><br><span class="line">    <span class="keyword">return</span> binary</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">recognize_plate</span><span class="params">(img_path)</span>:</span></span><br><span class="line">    img = cv2.imread(img_path)</span><br><span class="line">    <span class="keyword">if</span> img <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        print(<span class="string">"圖片讀取失敗"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    candidates = find_plate_candidates(img)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> candidates:</span><br><span class="line">        print(<span class="string">"未找到車牌候選區域"</span>)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 取面積最大的候選框</span></span><br><span class="line">    plate_region, (x, y, w, h) = crop_plate(img, candidates[<span class="number">0</span>])</span><br><span class="line">    processed = preprocess_plate(plate_region)</span><br><span class="line"></span><br><span class="line">    results = reader.readtext(processed, allowlist=<span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"</span>)</span><br><span class="line"></span><br><span class="line">    plate_text = <span class="string">" "</span>.join([r[<span class="number">1</span>] <span class="keyword">for</span> r <span class="keyword">in</span> results <span class="keyword">if</span> r[<span class="number">2</span>] &gt; <span class="number">0.3</span>])</span><br><span class="line">    print(<span class="string">f"辨識結果：<span class="subst">&#123;plate_text <span class="keyword">if</span> plate_text <span class="keyword">else</span> <span class="string">'無法辨識'</span>&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 繪製標記</span></span><br><span class="line">    cv2.rectangle(img, (x, y), (x + w, y + h), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">3</span>)</span><br><span class="line">    cv2.putText(img, plate_text, (x, y - <span class="number">10</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">1.0</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Result"</span>, img)</span><br><span class="line">    cv2.imshow(<span class="string">"Plate"</span>, processed)</span><br><span class="line">    cv2.waitKey(<span class="number">0</span>)</span><br><span class="line">    cv2.destroyAllWindows()</span><br><span class="line"></span><br><span class="line">recognize_plate(<span class="string">"assets/license_plate.jpg"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-license-plate/01.png" alt="偵測圖片中的車牌輪廓區域，透過 Otsu 二值化前處理後使用 EasyOCR 辨識車牌號碼"><br><em>圖：偵測圖片中的車牌輪廓區域，透過 Otsu 二值化前處理後使用 EasyOCR 辨識車牌號碼</em></p><h2 id="💻-步驟二：改善辨識率的前處理"><a href="#💻-步驟二：改善辨識率的前處理" class="headerlink" title="💻 步驟二：改善辨識率的前處理"></a>💻 步驟二：改善辨識率的前處理</h2><p>對四角點車牌區域進行透視矯正，將傾斜或仰角拍攝的車牌展平為正面視角​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p><p>光線與角度影響辨識率很大，可加入透視矯正提升準確度：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># perspective_transform.py</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">four_point_transform</span><span class="params">(img, pts)</span>:</span></span><br><span class="line">    <span class="string">"""將四個角點矩形區域進行透視矯正"""</span></span><br><span class="line">    pts = pts.reshape(<span class="number">4</span>, <span class="number">2</span>).astype(np.float32)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 排序角點：左上、右上、右下、左下</span></span><br><span class="line">    s    = pts.sum(axis=<span class="number">1</span>)</span><br><span class="line">    diff = np.diff(pts, axis=<span class="number">1</span>)</span><br><span class="line">    rect = np.array([</span><br><span class="line">        pts[np.argmin(s)],    <span class="comment"># 左上</span></span><br><span class="line">        pts[np.argmin(diff)], <span class="comment"># 右上</span></span><br><span class="line">        pts[np.argmax(s)],    <span class="comment"># 右下</span></span><br><span class="line">        pts[np.argmax(diff)], <span class="comment"># 左下</span></span><br><span class="line">    ], dtype=np.float32)</span><br><span class="line"></span><br><span class="line">    w = max(</span><br><span class="line">        np.linalg.norm(rect[<span class="number">1</span>] - rect[<span class="number">0</span>]),</span><br><span class="line">        np.linalg.norm(rect[<span class="number">2</span>] - rect[<span class="number">3</span>]),</span><br><span class="line">    )</span><br><span class="line">    h = max(</span><br><span class="line">        np.linalg.norm(rect[<span class="number">3</span>] - rect[<span class="number">0</span>]),</span><br><span class="line">        np.linalg.norm(rect[<span class="number">2</span>] - rect[<span class="number">1</span>]),</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    dst = np.array([[<span class="number">0</span>, <span class="number">0</span>], [w, <span class="number">0</span>], [w, h], [<span class="number">0</span>, h]], dtype=np.float32)</span><br><span class="line">    M   = cv2.getPerspectiveTransform(rect, dst)</span><br><span class="line">    <span class="keyword">return</span> cv2.warpPerspective(img, M, (int(w), int(h)))</span><br></pre></td></tr></table></figure><h2 id="💻-步驟三：從影片或串流辨識車牌"><a href="#💻-步驟三：從影片或串流辨識車牌" class="headerlink" title="💻 步驟三：從影片或串流辨識車牌"></a>💻 步驟三：從影片或串流辨識車牌</h2><p>步驟一的輪廓法（<code>approxPolyDP</code>）適合乾淨的車牌近照，但在真實行車影片中背景雜訊過多，常常找不到四邊形或找錯位置，再加上只取「面積最大」的候選框，命中率很低。</p><p>本步驟改為<strong>直接把整幀交給 EasyOCR 內建的文字偵測模型（CRAFT）偵測所有文字區塊，再用信心度與長寬比挑出最像車牌的那一塊</strong>，比輪廓法穩定很多。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p><p>同時以影片檔或 MJPEG 串流取代即時攝影機，不需要攝影機設備也不涉及個人隱私即可完整練習。建議使用行車記錄器影片、停車場監控影片或自行拍攝的車輛影片作為測試素材；若需要即時畫面，也可改用手機 App（例如 IP Webcam）提供的 MJPEG 串流 URL，程式邏輯完全相同。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># scan_video.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> easyocr</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">reader      = easyocr.Reader([<span class="string">"en"</span>], gpu=<span class="literal">False</span>)</span><br><span class="line">video_path  = <span class="string">"assets/traffic.mp4"</span>   <span class="comment"># 影片檔或 MJPEG 串流 URL 皆可</span></span><br><span class="line">cap         = cv2.VideoCapture(video_path)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cap.isOpened():</span><br><span class="line">    print(<span class="string">f"無法開啟影片：<span class="subst">&#123;video_path&#125;</span>"</span>)</span><br><span class="line">    exit()</span><br><span class="line"></span><br><span class="line">last_result = <span class="string">""</span></span><br><span class="line">last_bbox   = <span class="literal">None</span></span><br><span class="line">last_time   = <span class="number">0</span></span><br><span class="line">interval    = <span class="number">1.0</span>   <span class="comment"># 每秒辨識一次，避免 OCR 頻繁呼叫拖慢幀率</span></span><br><span class="line"></span><br><span class="line">print(<span class="string">"影片辨識啟動，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        print(<span class="string">"影片播放完畢"</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    now = time.time()</span><br><span class="line">    <span class="keyword">if</span> now - last_time &gt; interval:</span><br><span class="line">        <span class="comment"># EasyOCR 直接在整幀畫面偵測文字，比輪廓法更穩定</span></span><br><span class="line">        results = reader.readtext(</span><br><span class="line">            frame,</span><br><span class="line">            allowlist=<span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"</span>,</span><br><span class="line">        )</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 過濾：信心度 &gt; 0.4、長寬比接近車牌比例（約 2:1 ~ 5:1）</span></span><br><span class="line">        best = <span class="literal">None</span></span><br><span class="line">        <span class="keyword">for</span> bbox, text, conf <span class="keyword">in</span> results:</span><br><span class="line">            <span class="keyword">if</span> conf &lt; <span class="number">0.4</span>:</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            (tl, tr, br, bl) = bbox</span><br><span class="line">            w = abs(tr[<span class="number">0</span>] - tl[<span class="number">0</span>])</span><br><span class="line">            h = abs(bl[<span class="number">1</span>] - tl[<span class="number">1</span>])</span><br><span class="line">            <span class="keyword">if</span> h == <span class="number">0</span> <span class="keyword">or</span> <span class="keyword">not</span> (<span class="number">1.5</span> &lt; w / h &lt; <span class="number">6</span>):</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">if</span> best <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">or</span> conf &gt; best[<span class="number">2</span>]:</span><br><span class="line">                best = (bbox, text.replace(<span class="string">" "</span>, <span class="string">""</span>), conf)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> best:</span><br><span class="line">            last_bbox, last_result, _ = best</span><br><span class="line">        last_time = now</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> last_bbox <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> last_result:</span><br><span class="line">        tl = last_bbox[<span class="number">0</span>]</span><br><span class="line">        br = last_bbox[<span class="number">2</span>]</span><br><span class="line">        pt1 = (int(tl[<span class="number">0</span>]), int(tl[<span class="number">1</span>]))</span><br><span class="line">        pt2 = (int(br[<span class="number">0</span>]), int(br[<span class="number">1</span>]))</span><br><span class="line">        cv2.rectangle(frame, pt1, pt2, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">        cv2.putText(frame, last_result, (pt1[<span class="number">0</span>], pt1[<span class="number">1</span>] - <span class="number">10</span>),</span><br><span class="line">                    cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.8</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"License Plate Recognition"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">30</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-license-plate/02.gif" alt="讀取行車影片，EasyOCR 在整幀畫面偵測所有文字後以信心度與長寬比過濾，標出最可能的車牌並顯示辨識結果"><br><em>圖：讀取行車影片，EasyOCR 在整幀畫面偵測所有文字後以信心度與長寬比過濾，標出最可能的車牌並顯示辨識結果</em></p><blockquote><p>📷 <strong>改用即時攝影機</strong>：將 <code>video_path = &quot;assets/traffic.mp4&quot;</code> 改為 <code>cap = cv2.VideoCapture(0)</code>，即可改為即時攝影機辨識，程式邏輯完全相同。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p></blockquote><blockquote><p>📡 <strong>改用 MJPEG 串流</strong>：將 <code>video_path</code> 改為 MJPEG URL（例如手機 App「IP Webcam」提供的 <code>http://&lt;手機 IP&gt;:8080/video</code>），即可從手機或 IP 攝影機取得即時畫面。</p></blockquote><blockquote><p>💡 <strong>效能提示</strong>：在整幀畫面跑 OCR 比裁切小區域慢，CPU 環境下一次約 0.5~2 秒；<code>interval = 1.0</code> 已經留了緩衝，若仍卡頓可調高到 <code>2.0</code>，或將畫面先縮小（<code>frame = cv2.resize(frame, (640, 360))</code>）再送 OCR。</p></blockquote><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>輪廓法的侷限性</strong>：<code>approxPolyDP</code> 找四邊形的方法在複雜背景下容易失效，若需要更穩健的車牌定位，建議改用 YOLOv8 訓練一個車牌偵測模型（定位更精準）。</li><li><strong>OCR 不設定 <code>allowlist</code> 會辨識出許多雜訊</strong>：台灣車牌只有英數字與 <code>-</code>，設定 <code>allowlist</code> 可大幅提升準確率。</li><li><strong>即時辨識不要每幀都跑 OCR</strong>：EasyOCR 速度較慢，用時間間隔（<code>interval</code>）控制呼叫頻率，避免幀率過低。</li><li><strong>繁體中文車牌</strong>：若需辨識含中文字（如機車牌），將 <code>easyocr.Reader([&quot;en&quot;])</code> 改為 <code>easyocr.Reader([&quot;ch_tra&quot;, &quot;en&quot;])</code>。</li><li><strong>MJPEG 串流延遲</strong>：透過網路讀取 MJPEG 串流時若出現畫面卡頓，可降低串流解析度或改回本機影片檔測試，避免網路狀況影響辨識結果。</li><li><strong>EasyOCR 安裝會覆蓋 OpenCV</strong>：若執行時出現 <code>cv2.imshow</code> 未實作或 <code>cv2 has no attribute &#39;imread&#39;</code> 等錯誤，請回頭依「套件安裝」的完整步驟清除所有 opencv 變體後再重新安裝 <code>opencv-contrib-python</code>。切記僅執行 <code>pip uninstall opencv-python-headless</code> 再安裝往往不夠，需一次移除所有變體才能避免殘留衝突。</li></ul><h2 id="📊-進階方向"><a href="#📊-進階方向" class="headerlink" title="📊 進階方向"></a>📊 進階方向</h2><ul><li><strong>用 YOLOv8 取代輪廓法定位車牌</strong>：準確度大幅提升，適合多車輛場景</li><li><strong>加入資料庫查詢</strong>：辨識到車牌後，查詢黑名單或收費系統</li><li><strong>多幀投票</strong>：同一輛車連續辨識 5 幀，取出現最多次的號碼作為最終結果，提升準確率</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>車牌辨識整合了輪廓分析、透視矯正與 OCR，是一個能實際部署的完整應用。<br>下一篇將進入 <a href="/python-opencv-20260416-python-opencv-project-image-classification"><strong>OpenCV 專案：小型圖片分類專案</strong></a>，把模型訓練篇學到的遷移學習技術整合成一個端對端的分類系統。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​‌​​‌​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​‌​‌</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://github.com/JaidedAI/EasyOCR" target="_blank" rel="external nofollow noopener noreferrer">EasyOCR GitHub</a><br><a href="https://docs.opencv.org/4.x/dd/d49/tutorial_py_contour_features.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV 官方文件 — Contour Features</a><br><a href="https://docs.opencv.org/4.x/da/d6e/tutorial_py_geometric_transformations.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV 官方文件 — Geometric Transformations</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260414-python-opencv-projec
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：即時人臉辨識系統</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260414-python-opencv-project-face-recognition/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260414-python-opencv-project-face-recognition/</id>
    <published>2026-04-14T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260413-python-opencv-project-filter-camera"><strong>即時濾鏡相機</strong></a> 中，我們完成了第一個整合專案。</p><p>這一篇進入更有挑戰性的應用：<strong>即時人臉辨識系統</strong>。<br>不只是「偵測到臉」，而是能辨識出「這張臉是誰」，並在即時畫面中顯示姓名與信心度。</p><p>這個專案不依賴深度學習框架，完全使用 OpenCV 內建的 LBPH（Local Binary Pattern Histogram）演算法即可完成。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>蒐集多人的人臉樣本</li><li>訓練 LBPH 辨識模型</li><li>影片辨識，顯示姓名與信心度</li><li>支援隨時新增人員（重新蒐集 + 重新訓練）</li></ul><h2 id="🗃️-專案結構"><a href="#🗃️-專案結構" class="headerlink" title="🗃️ 專案結構"></a>🗃️ 專案結構</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">face_recognition&#x2F;</span><br><span class="line">├── assets&#x2F;          ← 素材影片（每人一段）</span><br><span class="line">│   ├── person_a.mp4</span><br><span class="line">│   ├── person_b.mp4</span><br><span class="line">│   └── test.mp4</span><br><span class="line">├── 1_collect.py     ← 步驟一：從影片蒐集人臉樣本</span><br><span class="line">├── 2_train.py       ← 步驟二：訓練辨識模型</span><br><span class="line">├── 3_recognize.py   ← 步驟三：影片辨識</span><br><span class="line">├── dataset&#x2F;         ← 蒐集的人臉圖片（自動建立）</span><br><span class="line">│   ├── 0_PersonA&#x2F;</span><br><span class="line">│   ├── 1_PersonB&#x2F;</span><br><span class="line">│   └── ...</span><br><span class="line">└── models&#x2F;          ← 儲存訓練好的模型（自動建立）</span><br><span class="line">    ├── face_model.yml</span><br><span class="line">    └── label_map.json</span><br></pre></td></tr></table></figure><h2 id="🎬-準備影片素材"><a href="#🎬-準備影片素材" class="headerlink" title="🎬 準備影片素材"></a>🎬 準備影片素材</h2><p>本篇以影片檔取代即時攝影機，不需要攝影機設備也不涉及個人隱私即可完整練習。</p><p>建議使用公開演講影片（例如 TED Talks，採 Creative Commons 授權），正面清晰、光線穩定，非常適合人臉辨識練習。每位人員各準備一段演講影片，另外再準備一段測試用影片（建議選不同段落，讓辨識測試更有說服力）。</p><p>使用 <code>yt-dlp</code> 下載：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># install_yt_dlp.sh</span></span><br><span class="line">pip install yt-dlp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 每位人員各下載一段演講影片</span></span><br><span class="line">yt-dlp -f <span class="string">"mp4[height&lt;=720]"</span> -o <span class="string">"assets/person_a.mp4"</span> [影片 URL]</span><br><span class="line">yt-dlp -f <span class="string">"mp4[height&lt;=720]"</span> -o <span class="string">"assets/person_b.mp4"</span> [影片 URL]</span><br></pre></td></tr></table></figure><p>此處示範使用 TED Talks 的兩部影片：</p><ul><li><a href="https://youtu.be/sb34MfJjurc?si=2EmEQuSZXCo7QOq_" target="_blank" rel="external nofollow noopener noreferrer">How Nearly Dying Helped Me Discover My Own Cure (and Many More) | David Fajgenbaum | TED</a></li><li><a href="https://youtu.be/whaesnYloMQ?si=GO29UO0xsB00NCgQ" target="_blank" rel="external nofollow noopener noreferrer">Why You Should Spend Less Time with Your Kids | Lenore Skenazy | TED</a><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">yt-dlp -f &quot;mp4[height&lt;&#x3D;720]&quot; -o &quot;assets&#x2F;person_a.mp4&quot; https:&#x2F;&#x2F;youtu.be&#x2F;sb34MfJjurc?si&#x3D;2EmEQuSZXCo7QOq_                                                                      </span><br><span class="line">yt-dlp -f &quot;mp4[height&lt;&#x3D;720]&quot; -o &quot;assets&#x2F;person_b.mp4&quot; https:&#x2F;&#x2F;youtu.be&#x2F;whaesnYloMQ?si&#x3D;GO29UO0xsB00NCgQ</span><br></pre></td></tr></table></figure></li><li>person_a 影片後處理兩段影片分別為訓練影片與驗證影片<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">40 - 60 秒  ⭢ person_a 00_00_40-00_01_00.mp4</span><br><span class="line">60 - 120 秒 ⭢ person_a 00_01_00-00_01_20.mp4</span><br></pre></td></tr></table></figure></li><li>person_b 影片後處理兩段影片分別為訓練影片與驗證影片<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">40 - 60 秒  ⭢ person_b 00_00_40-00_01_00.mp4</span><br><span class="line">60 - 120 秒 ⭢ person_b 00_01_00-00_01_20.mp4</span><br></pre></td></tr></table></figure></li></ul><h2 id="💻-步驟一：蒐集人臉樣本"><a href="#💻-步驟一：蒐集人臉樣本" class="headerlink" title="💻 步驟一：蒐集人臉樣本"></a>💻 步驟一：蒐集人臉樣本</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1_collect.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># ===== 設定 =====</span></span><br><span class="line">person_id      = <span class="number">0</span>                       <span class="comment"># 每位人員指定一個唯一 ID（從 0 開始遞增）</span></span><br><span class="line">person_name    = <span class="string">"PersonA"</span>               <span class="comment"># 人員姓名</span></span><br><span class="line">video_path     = <span class="string">"assets/person_a.mp4"</span>   <span class="comment"># 來源影片路徑</span></span><br><span class="line">sample_count   = <span class="number">100</span>                     <span class="comment"># 目標蒐集樣本數</span></span><br><span class="line">frame_interval = <span class="number">3</span>                       <span class="comment"># 每 N 幀處理一次，避免連續幀過於相似</span></span><br><span class="line"><span class="comment"># ================</span></span><br><span class="line"></span><br><span class="line">save_dir = <span class="string">f"dataset/<span class="subst">&#123;person_id&#125;</span>_<span class="subst">&#123;person_name&#125;</span>"</span></span><br><span class="line">os.makedirs(save_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">face_cascade = cv2.CascadeClassifier(</span><br><span class="line">    cv2.data.haarcascades + <span class="string">"haarcascade_frontalface_default.xml"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(video_path)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cap.isOpened():</span><br><span class="line">    print(<span class="string">f"無法開啟影片：<span class="subst">&#123;video_path&#125;</span>"</span>)</span><br><span class="line">    exit()</span><br><span class="line"></span><br><span class="line">count     = <span class="number">0</span></span><br><span class="line">frame_idx = <span class="number">0</span></span><br><span class="line">print(<span class="string">f"開始從影片蒐集 <span class="subst">&#123;person_name&#125;</span> 的人臉樣本，目標 <span class="subst">&#123;sample_count&#125;</span> 張"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> count &lt; sample_count:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        print(<span class="string">"影片播放完畢"</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    frame_idx += <span class="number">1</span></span><br><span class="line">    <span class="keyword">if</span> frame_idx % frame_interval != <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    gray  = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    faces = face_cascade.detectMultiScale(gray, <span class="number">1.3</span>, <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (x, y, w, h) <span class="keyword">in</span> faces:</span><br><span class="line">        face_img = gray[y:y+h, x:x+w]</span><br><span class="line">        face_rsz = cv2.resize(face_img, (<span class="number">200</span>, <span class="number">200</span>))</span><br><span class="line">        filename = os.path.join(save_dir, <span class="string">f"<span class="subst">&#123;count:<span class="number">04</span>d&#125;</span>.jpg"</span>)</span><br><span class="line">        cv2.imwrite(filename, face_rsz)</span><br><span class="line">        count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">        cv2.rectangle(frame, (x, y), (x+w, y+h), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">        cv2.putText(frame, <span class="string">f"<span class="subst">&#123;count&#125;</span>/<span class="subst">&#123;sample_count&#125;</span>"</span>, (x, y - <span class="number">10</span>),</span><br><span class="line">                    cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.7</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Collecting"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">30</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br><span class="line">print(<span class="string">f"完成！共蒐集 <span class="subst">&#123;count&#125;</span> 張樣本，儲存於 <span class="subst">&#123;save_dir&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-face-recognition/01.gif" alt="從影片逐幀偵測人臉，每隔 N 幀截取一次，自動儲存灰階人臉圖片，累積到指定樣本數後結束"><br><em>圖：從影片逐幀偵測人臉，每隔 N 幀截取一次，自動儲存灰階人臉圖片，累積到指定樣本數後結束</em></p><blockquote><p>💡 每位人員執行一次，修改 <code>person_id</code>、<code>person_name</code> 與 <code>video_path</code> 後重新執行，即可累積多人資料集。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p></blockquote><blockquote><p>📷 <strong>改用即時攝影機</strong>：將 <code>video_path</code> 那行改為 <code>cap = cv2.VideoCapture(0)</code>，並移除 <code>frame_interval</code> 的跳幀邏輯（或設為 <code>1</code>），即可改用攝影機即時蒐集，程式邏輯完全相同。</p></blockquote><h2 id="💻-步驟二：訓練辨識模型"><a href="#💻-步驟二：訓練辨識模型" class="headerlink" title="💻 步驟二：訓練辨識模型"></a>💻 步驟二：訓練辨識模型</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 2_train.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line">dataset_dir = <span class="string">"dataset"</span></span><br><span class="line">faces       = []</span><br><span class="line">labels      = []</span><br><span class="line">label_map   = &#123;&#125;    <span class="comment"># id → name</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> folder <span class="keyword">in</span> sorted(os.listdir(dataset_dir)):</span><br><span class="line">    folder_path = os.path.join(dataset_dir, folder)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> os.path.isdir(folder_path):</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 資料夾命名規則：&#123;id&#125;_&#123;name&#125;</span></span><br><span class="line">    parts       = folder.split(<span class="string">"_"</span>, <span class="number">1</span>)</span><br><span class="line">    person_id   = int(parts[<span class="number">0</span>])</span><br><span class="line">    person_name = parts[<span class="number">1</span>] <span class="keyword">if</span> len(parts) &gt; <span class="number">1</span> <span class="keyword">else</span> folder</span><br><span class="line">    label_map[person_id] = person_name</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> fname <span class="keyword">in</span> os.listdir(folder_path):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> fname.lower().endswith(<span class="string">".jpg"</span>):</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        img_path = os.path.join(folder_path, fname)</span><br><span class="line">        img      = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)</span><br><span class="line">        <span class="keyword">if</span> img <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">            faces.append(img)</span><br><span class="line">            labels.append(person_id)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"載入完成：<span class="subst">&#123;len(faces)&#125;</span> 張樣本，<span class="subst">&#123;len(label_map)&#125;</span> 位人員"</span>)</span><br><span class="line">print(<span class="string">f"人員清單：<span class="subst">&#123;label_map&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">recognizer = cv2.face.LBPHFaceRecognizer_create()</span><br><span class="line">recognizer.train(faces, np.array(labels))</span><br><span class="line"></span><br><span class="line">os.makedirs(<span class="string">"models"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">recognizer.save(<span class="string">"models/face_model.yml"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"models/label_map.json"</span>, <span class="string">"w"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    json.dump(&#123;str(k): v <span class="keyword">for</span> k, v <span class="keyword">in</span> label_map.items()&#125;, f, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"訓練完成！模型已儲存至 model/face_model.yml"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-face-recognition/02.gif" alt="讀取所有人員的人臉樣本，使用 LBPH 演算法訓練辨識模型並儲存為 yml 與 JSON 標籤檔"><br><em>圖：讀取所有人員的人臉樣本，使用 LBPH 演算法訓練辨識模型並儲存為 yml 與 JSON 標籤檔</em></p><blockquote><p>⚠️ <code>cv2.face</code> 需要 <code>opencv-contrib-python</code>：<code>pip install opencv-contrib-python</code>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p></blockquote><h2 id="💻-步驟三：影片辨識"><a href="#💻-步驟三：影片辨識" class="headerlink" title="💻 步驟三：影片辨識"></a>💻 步驟三：影片辨識</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 3_recognize.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 載入模型與標籤</span></span><br><span class="line">recognizer = cv2.face.LBPHFaceRecognizer_create()</span><br><span class="line">recognizer.read(<span class="string">"models/face_model.yml"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"models/label_map.json"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    raw_map   = json.load(f)</span><br><span class="line">    label_map = &#123;int(k): v <span class="keyword">for</span> k, v <span class="keyword">in</span> raw_map.items()&#125;</span><br><span class="line"></span><br><span class="line">face_cascade = cv2.CascadeClassifier(</span><br><span class="line">    cv2.data.haarcascades + <span class="string">"haarcascade_frontalface_default.xml"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">CONFIDENCE_THRESHOLD = <span class="number">80</span></span><br><span class="line">video_path = <span class="string">"assets/test.mp4"</span></span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(video_path)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cap.isOpened():</span><br><span class="line">    print(<span class="string">f"無法開啟影片：<span class="subst">&#123;video_path&#125;</span>"</span>)</span><br><span class="line">    exit()</span><br><span class="line">print(<span class="string">"影片辨識啟動，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        print(<span class="string">"影片播放完畢"</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    gray  = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    faces = face_cascade.detectMultiScale(gray, <span class="number">1.3</span>, <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (x, y, w, h) <span class="keyword">in</span> faces:</span><br><span class="line">        face_img = gray[y:y+h, x:x+w]</span><br><span class="line">        face_rsz = cv2.resize(face_img, (<span class="number">200</span>, <span class="number">200</span>))</span><br><span class="line"></span><br><span class="line">        label, confidence = recognizer.predict(face_rsz)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># LBPH：confidence 越低代表越相似（0 = 完全一致）</span></span><br><span class="line">        <span class="keyword">if</span> confidence &lt; CONFIDENCE_THRESHOLD:</span><br><span class="line">            name  = label_map.get(label, <span class="string">"Unknown"</span>)</span><br><span class="line">            color = (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>)</span><br><span class="line">            text  = <span class="string">f"<span class="subst">&#123;name&#125;</span> (<span class="subst">&#123;confidence:<span class="number">.1</span>f&#125;</span>)"</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            name  = <span class="string">"Unknown"</span></span><br><span class="line">            color = (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>)</span><br><span class="line">            text  = <span class="string">f"Unknown (<span class="subst">&#123;confidence:<span class="number">.1</span>f&#125;</span>)"</span></span><br><span class="line"></span><br><span class="line">        cv2.rectangle(frame, (x, y), (x+w, y+h), color, <span class="number">2</span>)</span><br><span class="line">        cv2.putText(frame, text, (x, y - <span class="number">10</span>),</span><br><span class="line">                    cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.7</span>, color, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Face Recognition"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">30</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-face-recognition/03.gif" alt="載入 LBPH 模型對測試影片進行辨識，依信心度顯示姓名或 Unknown 並標示邊界框"><br><em>圖：載入 LBPH 模型對測試影片進行辨識，依信心度顯示姓名或 Unknown 並標示邊界框</em></p><blockquote><p>📷 <strong>改用即時攝影機</strong>：將 <code>video_path = &quot;assets/test.mp4&quot;</code> 改為 <code>cap = cv2.VideoCapture(0)</code>，即可改為即時攝影機辨識，程式邏輯完全相同。</p></blockquote><h2 id="💻-新增人員"><a href="#💻-新增人員" class="headerlink" title="💻 新增人員"></a>💻 新增人員</h2><p>只需：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><ol><li>修改 <code>1_collect.py</code> 的 <code>person_id</code>（遞增）、<code>person_name</code> 與 <code>video_path</code>，重新執行蒐集</li><li>重新執行 <code>2_train.py</code> 訓練（會自動載入所有已蒐集的資料）</li><li>重新執行 <code>3_recognize.py</code></li></ol><p>不需要修改任何辨識邏輯。</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>影片品質</strong>：蒐集樣本的影片建議選正面清晰、光線均勻的片段，避免快速搖晃或逆光。</li><li><strong>LBPH 對光線敏感</strong>：蒐集影片與測試影片的光線環境差異越小，辨識率越高。</li><li><strong>信心度閾值需要調整</strong>：<code>CONFIDENCE_THRESHOLD = 80</code> 是參考值，不同影片品質與光線條件可能需要調高或調低。</li><li><strong>樣本多樣性</strong>：若影片角度過於單一，辨識率可能偏低，建議選取包含多種頭部角度的片段。</li><li><strong><code>frame_interval</code> 設定</strong>：影片較短但樣本需求量大時可調低；角度變化緩慢時可調高以增加多樣性。</li><li><strong><code>opencv-contrib-python</code> 與 <code>opencv-python</code> 不能同時安裝</strong>：若已安裝 <code>opencv-python</code>，需先 <code>pip uninstall opencv-python</code> 再安裝 <code>opencv-contrib-python</code>。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>LBPH 人臉辨識輕量且不需要 GPU，適合在本機快速建立小規模的人員辨識系統。<br>若需要更高準確度（例如跨光線、跨角度），可考慮改用 face_recognition 套件（基於深度學習的人臉嵌入向量）。</p><p>下一篇將進入 <a href="/python-opencv-20260415-python-opencv-project-license-plate"><strong>OpenCV 專案：車牌辨識應用</strong></a>，結合輪廓分析與 OCR，識別圖片中的車牌號碼。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​‌​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌​​​‌‌​‌‌​​‌​‌​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌‌‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌‌​‌​​​‌‌​‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌‌​</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.opencv.org/4.x/da/d60/tutorial_face_main.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV 官方文件 — Face Recognition</a><br><a href="https://docs.opencv.org/4.x/df/d25/classcv_1_1face_1_1LBPHFaceRecognizer.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV LBPH Face Recognizer</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260413-python-opencv-projec
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 專案：即時濾鏡相機</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260413-python-opencv-project-filter-camera/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260413-python-opencv-project-filter-camera/</id>
    <published>2026-04-13T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260412-python-opencv-qrcode-barcode"><strong>OpenCV QR Code 與 BarCode 辨識</strong></a> 中，我們完成了條碼辨識的實作，正式踏入 <strong>專案實作篇</strong>。</p><p>這一篇是第一個整合專案：<strong>即時濾鏡相機</strong>。<br>目標是把前面學過的色彩轉換、模糊、形態學、繪圖等功能整合在一起，做出一個可用鍵盤切換濾鏡的即時相機程式。沒有攝影機也沒關係，程式同時支援以影片檔作為輸入來源，效果完全相同。</p><h2 id="🎯-專案目標"><a href="#🎯-專案目標" class="headerlink" title="🎯 專案目標"></a>🎯 專案目標</h2><ul><li>攝影機或影片檔即時預覽</li><li>按鍵切換 10 種以上的濾鏡效果</li><li>畫面顯示目前濾鏡名稱</li><li>按 <code>s</code> 截圖儲存</li><li>按 <code>q</code> 離開</li></ul><h2 id="🗃️-專案結構"><a href="#🗃️-專案結構" class="headerlink" title="🗃️ 專案結構"></a>🗃️ 專案結構</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">filter_camera&#x2F;</span><br><span class="line">├── assets&#x2F;          ← 測試影片（無攝影機時使用）</span><br><span class="line">│   └── sample.mp4</span><br><span class="line">├── output&#x2F;          ← 截圖輸出</span><br><span class="line">├── main.py          ← 主程式</span><br><span class="line">└── filters.py       ← 濾鏡函式集合</span><br></pre></td></tr></table></figure><h2 id="🎨-範例影片"><a href="#🎨-範例影片" class="headerlink" title="🎨 範例影片"></a>🎨 範例影片</h2><ul><li>來源：<a href="https://www.pexels.com/zh-tw/video/29458457/" target="_blank" rel="external nofollow noopener noreferrer">Pexels - Code</a>，屬於無版權影片，可自由下載與使用。</li><li>下載後將檔名改為 <code>sample.mp4</code>，放到專案的 <code>assets/</code> 目錄下。</li></ul><h2 id="💻-filters-py-—-濾鏡函式集合"><a href="#💻-filters-py-—-濾鏡函式集合" class="headerlink" title="💻 filters.py — 濾鏡函式集合"></a>💻 filters.py — 濾鏡函式集合</h2><ul><li>定義灰階、模糊、邊緣、復古、負片、銳化、浮雕、卡通、馬賽克、暖冷色調等 12 種濾鏡函式</li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># filters.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">original</span><span class="params">(frame)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> frame</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">grayscale</span><span class="params">(frame)</span>:</span></span><br><span class="line">    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    <span class="keyword">return</span> cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">blur</span><span class="params">(frame)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> cv2.GaussianBlur(frame, (<span class="number">21</span>, <span class="number">21</span>), <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">edge</span><span class="params">(frame)</span>:</span></span><br><span class="line">    gray  = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    edges = cv2.Canny(gray, <span class="number">50</span>, <span class="number">150</span>)</span><br><span class="line">    <span class="keyword">return</span> cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sepia</span><span class="params">(frame)</span>:</span></span><br><span class="line">    kernel = np.array([[<span class="number">0.272</span>, <span class="number">0.534</span>, <span class="number">0.131</span>],</span><br><span class="line">                       [<span class="number">0.349</span>, <span class="number">0.686</span>, <span class="number">0.168</span>],</span><br><span class="line">                       [<span class="number">0.393</span>, <span class="number">0.769</span>, <span class="number">0.189</span>]])</span><br><span class="line">    result = cv2.transform(frame.astype(np.float32), kernel)</span><br><span class="line">    <span class="keyword">return</span> np.clip(result, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">invert</span><span class="params">(frame)</span>:</span></span><br><span class="line">    <span class="keyword">return</span> cv2.bitwise_not(frame)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sharpen</span><span class="params">(frame)</span>:</span></span><br><span class="line">    kernel = np.array([[ <span class="number">0</span>, <span class="number">-1</span>,  <span class="number">0</span>],</span><br><span class="line">                       [<span class="number">-1</span>,  <span class="number">5</span>, <span class="number">-1</span>],</span><br><span class="line">                       [ <span class="number">0</span>, <span class="number">-1</span>,  <span class="number">0</span>]])</span><br><span class="line">    <span class="keyword">return</span> cv2.filter2D(frame, <span class="number">-1</span>, kernel)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">emboss</span><span class="params">(frame)</span>:</span></span><br><span class="line">    kernel = np.array([[<span class="number">-2</span>, <span class="number">-1</span>,  <span class="number">0</span>],</span><br><span class="line">                       [<span class="number">-1</span>,  <span class="number">1</span>,  <span class="number">1</span>],</span><br><span class="line">                       [ <span class="number">0</span>,  <span class="number">1</span>,  <span class="number">2</span>]])</span><br><span class="line">    result = cv2.filter2D(frame, <span class="number">-1</span>, kernel) + <span class="number">128</span></span><br><span class="line">    <span class="keyword">return</span> np.clip(result, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cartoon</span><span class="params">(frame)</span>:</span></span><br><span class="line">    gray     = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)</span><br><span class="line">    gray_blur = cv2.medianBlur(gray, <span class="number">5</span>)</span><br><span class="line">    edges    = cv2.adaptiveThreshold(gray_blur, <span class="number">255</span>,</span><br><span class="line">                                     cv2.ADAPTIVE_THRESH_MEAN_C,</span><br><span class="line">                                     cv2.THRESH_BINARY, <span class="number">9</span>, <span class="number">9</span>)</span><br><span class="line">    color    = cv2.bilateralFilter(frame, <span class="number">9</span>, <span class="number">300</span>, <span class="number">300</span>)</span><br><span class="line">    <span class="keyword">return</span> cv2.bitwise_and(color, color, mask=edges)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">mosaic</span><span class="params">(frame, block=<span class="number">20</span>)</span>:</span></span><br><span class="line">    h, w  = frame.shape[:<span class="number">2</span>]</span><br><span class="line">    small = cv2.resize(frame, (w // block, h // block),</span><br><span class="line">                       interpolation=cv2.INTER_LINEAR)</span><br><span class="line">    <span class="keyword">return</span> cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">warm</span><span class="params">(frame)</span>:</span></span><br><span class="line">    <span class="comment"># 提高紅/黃色調</span></span><br><span class="line">    lut = np.arange(<span class="number">256</span>, dtype=np.uint8)</span><br><span class="line">    lut_r = np.clip(lut * <span class="number">1.2</span>, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line">    lut_b = np.clip(lut * <span class="number">0.8</span>, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line">    b, g, r = cv2.split(frame)</span><br><span class="line">    <span class="keyword">return</span> cv2.merge([cv2.LUT(b, lut_b), g, cv2.LUT(r, lut_r)])</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cool</span><span class="params">(frame)</span>:</span></span><br><span class="line">    <span class="comment"># 提高藍色調</span></span><br><span class="line">    lut   = np.arange(<span class="number">256</span>, dtype=np.uint8)</span><br><span class="line">    lut_r = np.clip(lut * <span class="number">0.8</span>, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line">    lut_b = np.clip(lut * <span class="number">1.2</span>, <span class="number">0</span>, <span class="number">255</span>).astype(np.uint8)</span><br><span class="line">    b, g, r = cv2.split(frame)</span><br><span class="line">    <span class="keyword">return</span> cv2.merge([cv2.LUT(b, lut_b), g, cv2.LUT(r, lut_r)])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 濾鏡清單：(名稱, 函式)</span></span><br><span class="line">FILTERS = [</span><br><span class="line">    (<span class="string">"原始畫面"</span>,     original),</span><br><span class="line">    (<span class="string">"灰階"</span>,         grayscale),</span><br><span class="line">    (<span class="string">"模糊"</span>,         blur),</span><br><span class="line">    (<span class="string">"邊緣"</span>,         edge),</span><br><span class="line">    (<span class="string">"復古"</span>,         sepia),</span><br><span class="line">    (<span class="string">"負片"</span>,         invert),</span><br><span class="line">    (<span class="string">"銳化"</span>,         sharpen),</span><br><span class="line">    (<span class="string">"浮雕"</span>,         emboss),</span><br><span class="line">    (<span class="string">"卡通"</span>,         cartoon),</span><br><span class="line">    (<span class="string">"馬賽克"</span>,       mosaic),</span><br><span class="line">    (<span class="string">"暖色調"</span>,       warm),</span><br><span class="line">    (<span class="string">"冷色調"</span>,       cool),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h2 id="💻-main-py-—-主程式"><a href="#💻-main-py-—-主程式" class="headerlink" title="💻 main.py — 主程式"></a>💻 main.py — 主程式</h2><p><code>cv2.VideoCapture()</code> 同時支援攝影機 index（整數）與影片檔路徑（字串），因此只需在開頭以 <code>sys.argv</code> 判斷來源，其餘邏輯完全不變。影片播完後以 <code>CAP_PROP_POS_FRAMES</code> seek 回第 0 幀即可無縫循環，濾鏡效果的呈現與真實攝影機沒有差異。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​‌​​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌​​‌​‌​‌‌‌​​‌​​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌​‌‌​‌​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​​​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># main.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">from</span> filters <span class="keyword">import</span> FILTERS</span><br><span class="line"></span><br><span class="line"><span class="comment"># python main.py                    → 攝影機 (index 0)</span></span><br><span class="line"><span class="comment"># python main.py assets/sample.mp4  → 影片檔</span></span><br><span class="line">source       = sys.argv[<span class="number">1</span>] <span class="keyword">if</span> len(sys.argv) &gt; <span class="number">1</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">is_video     = isinstance(source, str)</span><br><span class="line">cap          = cv2.VideoCapture(source)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cap.isOpened():</span><br><span class="line">    print(<span class="string">"無法開啟來源，請確認攝影機或影片路徑是否正確"</span>)</span><br><span class="line">    exit()</span><br><span class="line">filter_index = <span class="number">0</span></span><br><span class="line">save_dir     = <span class="string">"output"</span></span><br><span class="line">os.makedirs(save_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"操作說明："</span>)</span><br><span class="line">print(<span class="string">"  ← / → 方向鍵  切換濾鏡"</span>)</span><br><span class="line">print(<span class="string">"  s              截圖儲存"</span>)</span><br><span class="line">print(<span class="string">"  q              離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">if</span> is_video:                           <span class="comment"># 影片播完後從頭循環</span></span><br><span class="line">            cap.set(cv2.CAP_PROP_POS_FRAMES, <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    name, func = FILTERS[filter_index]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        output = func(frame)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        print(<span class="string">f"濾鏡 [<span class="subst">&#123;name&#125;</span>] 發生錯誤：<span class="subst">&#123;e&#125;</span>"</span>)</span><br><span class="line">        output = frame</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Filter Camera"</span>, output)</span><br><span class="line"></span><br><span class="line">    key = cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> key == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">elif</span> key == ord(<span class="string">"s"</span>):</span><br><span class="line">        ts       = datetime.now().strftime(<span class="string">"%Y%m%d_%H%M%S"</span>)</span><br><span class="line">        filename = os.path.join(save_dir, <span class="string">f"<span class="subst">&#123;ts&#125;</span>.jpg"</span>)</span><br><span class="line">        cv2.imwrite(filename, output)</span><br><span class="line">        print(<span class="string">f"截圖已儲存：<span class="subst">&#123;filename&#125;</span>"</span>)</span><br><span class="line">    <span class="keyword">elif</span> key == <span class="number">81</span> <span class="keyword">or</span> key == ord(<span class="string">"a"</span>):   <span class="comment"># ← 或 a</span></span><br><span class="line">        filter_index = (filter_index - <span class="number">1</span>) % len(FILTERS)</span><br><span class="line">    <span class="keyword">elif</span> key == <span class="number">83</span> <span class="keyword">or</span> key == ord(<span class="string">"d"</span>):   <span class="comment"># → 或 d</span></span><br><span class="line">        filter_index = (filter_index + <span class="number">1</span>) % len(FILTERS)</span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/project-filter-camera/01.gif" alt="開啟影片檔，以方向鍵或是(a / d)切換濾鏡並在畫面顯示名稱，按 s 截圖儲存，按 q 離開"><br><em>圖：開啟影片檔，以方向鍵或是(a / d)切換濾鏡並在畫面顯示名稱，按 s 截圖儲存，按 q 離開</em></p><h2 id="💻-執行方式"><a href="#💻-執行方式" class="headerlink" title="💻 執行方式"></a>💻 執行方式</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> filter_camera</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用攝影機</span></span><br><span class="line">python main.py</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用影片檔（不需攝影機）</span></span><br><span class="line">python main.py assets/sample.mp4</span><br></pre></td></tr></table></figure><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>影片檔模式自動循環</strong>：影片播完會自動從頭播放，適合無攝影機環境展示所有濾鏡效果；若要單次播放，移除 <code>if is_video</code> 的 seek 邏輯即可。</li><li><strong>方向鍵的 keycode 因平台而異</strong>：Windows 上 <code>←</code> 為 <code>81</code>、<code>→</code> 為 <code>83</code>；若方向鍵無效，可改用 <code>a</code> / <code>d</code> 切換。</li><li><strong>卡通濾鏡速度較慢</strong>：<code>bilateralFilter</code> 計算量大，幀率可能下降，可縮小 <code>d</code>（直徑）參數或降低解析度改善。</li><li><strong>截圖目錄</strong>：預設儲存在執行目錄下的 <code>output/</code>，初次執行會自動建立。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>即時濾鏡相機整合了色彩轉換、濾波、形態學、LUT 色調調整等多種技術，是一個能快速驗證學習成果的入門專案。</p><p>下一篇將進入 <a href="/python-opencv-20260414-python-opencv-project-face-recognition"><strong>OpenCV 專案：即時人臉辨識系統</strong></a>，把人臉偵測進一步升級為能辨識具體身份的系統。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​‌‌‌‌​‌‌​‌​‌​​‌‌​​‌​‌​‌‌​​​‌‌​‌‌‌​‌​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​‌​​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌​​‌​‌​‌‌‌​​‌​​​‌​‌‌​‌​‌‌​​​‌‌​‌‌​​​​‌​‌‌​‌‌​‌​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​​​‌</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.opencv.org/4.x/d4/d13/tutorial_py_filtering.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV 官方文件 — Image Filtering</a><br><a href="https://docs.opencv.org/4.x/df/d9d/tutorial_py_colorspaces.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV 官方文件 — Color Conversions</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260412-python-opencv-qrcode
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV QR Code 與 BarCode 辨識</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260412-python-opencv-qrcode-barcode/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260412-python-opencv-qrcode-barcode/</id>
    <published>2026-04-12T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在 <a href="/python-opencv-20260405-python-opencv-inference-with-opencv"><strong>與 OpenCV 整合推論</strong></a> 中，我們學會了如何把深度學習模型整合進 OpenCV 的視訊流程。</p><p>這一篇介紹一個非常實用的應用：<strong>QR Code 與 BarCode 辨識</strong>。<br>從商品掃碼、入場驗票到倉儲管理，條碼掃描是日常中最常見的電腦視覺應用之一，而且實作起來相對簡單。</p><h2 id="🎨-範例圖片"><a href="#🎨-範例圖片" class="headerlink" title="🎨 範例圖片"></a>🎨 範例圖片</h2><p>這裡我們使用 BarcodeOcean 提供的條碼產生器：<a href="https://www.barcodeocean.com/zh" target="_blank" rel="external nofollow noopener noreferrer">BarcodeOcean</a>。<br>使用「生成QR Code」產生後，下載將檔名改為 <code>qrcode.png</code>，放在 <code>assets/</code>，即可用於以下各範例。<br>使用「生成條碼」產生後，下載將檔名改為 <code>Barcode.png</code>，放在 <code>assets/</code>，即可用於以下各範例。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><h2 id="🛠️-套件安裝"><a href="#🛠️-套件安裝" class="headerlink" title="🛠️ 套件安裝"></a>🛠️ 套件安裝</h2><p>OpenCV 內建 <code>QRCodeDetector</code> 可解碼 QR Code，Barcode 辨識則建議搭配 <code>pyzbar</code> 套件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install pyzbar</span><br><span class="line">pip install qrcode[pil]   <span class="comment"># 若需要產生 QR Code</span></span><br></pre></td></tr></table></figure><blockquote><p>💡 Windows 使用者若安裝 pyzbar 後出現 <code>zbar DLL not found</code> 錯誤，需另外下載 zbar DLL：<br>可安裝 <code>pip install pyzbar-win32-fix</code> 或從 <a href="http://zbar.sourceforge.net/" target="_blank" rel="external nofollow noopener noreferrer">zbar 官網</a> 下載 DLL 後放入 <code>%SystemRoot%\System32</code>。</p></blockquote><h2 id="💻-使用-OpenCV-解碼-QR-Code"><a href="#💻-使用-OpenCV-解碼-QR-Code" class="headerlink" title="💻 使用 OpenCV 解碼 QR Code"></a>💻 使用 OpenCV 解碼 QR Code</h2><p>OpenCV 內建 <code>cv2.QRCodeDetector</code>，不需要額外套件即可解碼 QR Code：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># decode_qrcode_opencv.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">img = cv2.imread(<span class="string">"assets/qrcode.png"</span>)</span><br><span class="line">detector = cv2.QRCodeDetector()</span><br><span class="line"></span><br><span class="line"><span class="comment"># detectAndDecode：同時偵測位置與解碼</span></span><br><span class="line"><span class="comment"># 回傳三個值：解碼字串、邊框座標、灰度圖；灰度圖不需要用到，以 _ 表示丟棄</span></span><br><span class="line">data, bbox, _ = detector.detectAndDecode(img)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> data:</span><br><span class="line">    print(<span class="string">f"QR Code 內容：<span class="subst">&#123;data&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># bbox 為四個角點座標，繪製邊框</span></span><br><span class="line">    <span class="keyword">if</span> bbox <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        bbox = bbox.astype(int)   <span class="comment"># bbox 預設為 float，繪圖函式需要整數座標</span></span><br><span class="line">        <span class="keyword">for</span> i <span class="keyword">in</span> range(len(bbox[<span class="number">0</span>])):</span><br><span class="line">            pt1 = tuple(bbox[<span class="number">0</span>][i])</span><br><span class="line">            pt2 = tuple(bbox[<span class="number">0</span>][(i + <span class="number">1</span>) % len(bbox[<span class="number">0</span>])])</span><br><span class="line">            cv2.line(img, pt1, pt2, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.putText(img, data, (<span class="number">10</span>, <span class="number">30</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.8</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    print(<span class="string">"未偵測到 QR Code"</span>)</span><br><span class="line"></span><br><span class="line">cv2.imshow(<span class="string">"QR Code"</span>, img)</span><br><span class="line">cv2.waitKey(<span class="number">0</span>)</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/01.png" alt="使用 OpenCV 內建 QRCodeDetector 解碼圖片中的 QR Code，繪製邊框並顯示解碼內容"><br><em>圖：使用 OpenCV 內建 QRCodeDetector 解碼圖片中的 QR Code，繪製邊框並顯示解碼內容</em></p><h2 id="💻-使用-pyzbar-解碼-QR-Code-與-BarCode"><a href="#💻-使用-pyzbar-解碼-QR-Code-與-BarCode" class="headerlink" title="💻 使用 pyzbar 解碼 QR Code 與 BarCode"></a>💻 使用 pyzbar 解碼 QR Code 與 BarCode</h2><p><code>pyzbar</code> 支援多種條碼格式（QR Code、Code128、EAN-13、EAN-8、UPC-A 等），是更通用的選擇：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># decode_barcode_pyzbar.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">from</span> pyzbar <span class="keyword">import</span> pyzbar</span><br><span class="line"></span><br><span class="line">img = cv2.imread(<span class="string">"assets/Barcode.png"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># decode 會回傳圖片中所有偵測到的條碼</span></span><br><span class="line">barcodes = pyzbar.decode(img)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> barcode <span class="keyword">in</span> barcodes:</span><br><span class="line">    <span class="comment"># barcode.data 是 bytes（二進位），需要 decode 成一般字串才能印出或比對</span></span><br><span class="line">    data      = barcode.data.decode(<span class="string">"utf-8"</span>)</span><br><span class="line">    bc_type   = barcode.type        <span class="comment"># 條碼格式，例如：QRCODE、CODE128、EAN13</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># barcode.rect：(x, y, w, h)</span></span><br><span class="line">    x, y, w, h = barcode.rect</span><br><span class="line">    cv2.rectangle(img, (x, y), (x + w, y + h), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    label = <span class="string">f"<span class="subst">&#123;bc_type&#125;</span>: <span class="subst">&#123;data&#125;</span>"</span></span><br><span class="line">    cv2.putText(img, label, (x, y - <span class="number">10</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.6</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">    print(<span class="string">f"類型：<span class="subst">&#123;bc_type&#125;</span>，內容：<span class="subst">&#123;data&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">cv2.imshow(<span class="string">"Barcode"</span>, img)</span><br><span class="line">cv2.waitKey(<span class="number">0</span>)</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/02.png" alt="使用 pyzbar 解碼圖片中的 QR Code 或 BarCode，繪製矩形框並輸出條碼類型與內容"><br><em>圖：使用 pyzbar 解碼圖片中的 QR Code 或 BarCode，繪製矩形框並輸出條碼類型與內容</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><h2 id="💻-即時攝影機掃描"><a href="#💻-即時攝影機掃描" class="headerlink" title="💻 即時攝影機掃描"></a>💻 即時攝影機掃描</h2><p>結合攝影機做即時掃描，是最常見的應用場景：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># scan_camera.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">from</span> pyzbar <span class="keyword">import</span> pyzbar</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)   <span class="comment"># 0 表示第一支攝影機；若有多支可改為 1、2…</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> cap.isOpened():</span><br><span class="line">    print(<span class="string">"無法開啟攝影機，請確認設備是否連接"</span>)</span><br><span class="line">    exit()</span><br><span class="line">print(<span class="string">"對準條碼，按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line">scanned = set()   <span class="comment"># 避免重複顯示同一條碼</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    barcodes = pyzbar.decode(frame)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> barcode <span class="keyword">in</span> barcodes:</span><br><span class="line">        data    = barcode.data.decode(<span class="string">"utf-8"</span>)</span><br><span class="line">        bc_type = barcode.type</span><br><span class="line"></span><br><span class="line">        x, y, w, h = barcode.rect</span><br><span class="line">        cv2.rectangle(frame, (x, y), (x + w, y + h), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">        cv2.putText(frame, <span class="string">f"<span class="subst">&#123;bc_type&#125;</span>: <span class="subst">&#123;data&#125;</span>"</span>, (x, y - <span class="number">10</span>),</span><br><span class="line">                    cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.6</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> data <span class="keyword">not</span> <span class="keyword">in</span> scanned:</span><br><span class="line">            scanned.add(data)</span><br><span class="line">            print(<span class="string">f"掃描到 [<span class="subst">&#123;bc_type&#125;</span>]：<span class="subst">&#123;data&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"Scanner"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><blockquote><p>📝 註：由於本身沒有攝影鏡頭，所以無法示範效果。</p></blockquote><h2 id="💻-批次處理圖片資料夾"><a href="#💻-批次處理圖片資料夾" class="headerlink" title="💻 批次處理圖片資料夾"></a>💻 批次處理圖片資料夾</h2><p>需要批次掃描大量圖片時（如倉儲盤點照片）：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># batch_scan.py</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> pyzbar <span class="keyword">import</span> pyzbar</span><br><span class="line"></span><br><span class="line">img_dir = <span class="string">"assets/"</span></span><br><span class="line">results = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> fname <span class="keyword">in</span> os.listdir(img_dir):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> fname.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".jpeg"</span>, <span class="string">".png"</span>)):</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    img      = cv2.imread(os.path.join(img_dir, fname))</span><br><span class="line">    barcodes = pyzbar.decode(img)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> barcode <span class="keyword">in</span> barcodes:</span><br><span class="line">        data    = barcode.data.decode(<span class="string">"utf-8"</span>)</span><br><span class="line">        bc_type = barcode.type</span><br><span class="line">        results.append(&#123;<span class="string">"file"</span>: fname, <span class="string">"type"</span>: bc_type, <span class="string">"data"</span>: data&#125;)</span><br><span class="line">        print(<span class="string">f"<span class="subst">&#123;fname&#125;</span>  [<span class="subst">&#123;bc_type&#125;</span>]  <span class="subst">&#123;data&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"\n共掃描 <span class="subst">&#123;len(results)&#125;</span> 筆條碼"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/03.png" alt="批次掃描目錄中的所有圖片，輸出每張圖片中偵測到的條碼類型、內容與統計筆數"><br><em>圖：批次掃描目錄中的所有圖片，輸出每張圖片中偵測到的條碼類型、內容與統計筆數</em></p><h2 id="💻-產生-QR-Code"><a href="#💻-產生-QR-Code" class="headerlink" title="💻 產生 QR Code"></a>💻 產生 QR Code</h2><p>使用 <code>qrcode</code> 套件產生自訂的 QR Code 圖片：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># generate_qrcode.py</span></span><br><span class="line"><span class="keyword">import</span> qrcode</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">output_dir = <span class="string">"output"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果 output 資料夾不存在，就自動建立</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(output_dir):</span><br><span class="line">    os.makedirs(output_dir)</span><br><span class="line">    print(<span class="string">f"已自動建立資料夾：<span class="subst">&#123;output_dir&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基本 QR Code</span></span><br><span class="line">data1 = <span class="string">"https://morosedog.gitlab.io/"</span></span><br><span class="line"></span><br><span class="line">img1 = qrcode.make(data1)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 存到 output 資料夾</span></span><br><span class="line">save_path1 = os.path.join(output_dir, <span class="string">"qrcode_basic.png"</span>)</span><br><span class="line">img1.save(save_path1)</span><br><span class="line">print(<span class="string">f"基本 QR Code 已儲存 → <span class="subst">&#123;save_path1&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自訂樣式 QR Code</span></span><br><span class="line">qr = qrcode.QRCode(</span><br><span class="line">    version=<span class="number">1</span>,</span><br><span class="line">    error_correction=qrcode.constants.ERROR_CORRECT_H,   <span class="comment"># 高容錯</span></span><br><span class="line">    box_size=<span class="number">10</span>,</span><br><span class="line">    border=<span class="number">4</span>,</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">qr.add_data(<span class="string">"Hello, OpenCV!"</span>)</span><br><span class="line">qr.make(fit=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">img2 = qr.make_image(fill_color=<span class="string">"black"</span>, back_color=<span class="string">"white"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 存到 output 資料夾</span></span><br><span class="line">save_path2 = os.path.join(output_dir, <span class="string">"qrcode_custom.png"</span>)</span><br><span class="line">img2.save(save_path2)</span><br><span class="line">print(<span class="string">f"自訂 QR Code 已儲存 → <span class="subst">&#123;save_path2&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"✅ 所有 QR Code 產生完成！"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/04.png" alt="使用 qrcode 套件產生基本與自訂樣式的 QR Code 圖片，設定容錯等級、格子大小與邊框"><br><em>圖：使用 qrcode 套件產生基本與自訂樣式的 QR Code 圖片，設定容錯等級、格子大小與邊框</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/qr_error_correction_levels.svg" alt="QR Code 錯誤修正等級（Error Correction Level）比較表，包含各等級的可容錯比例與建議使用情境"><br><em>圖：QR Code 錯誤修正等級（Error Correction Level）比較表，包含各等級的可容錯比例與建議使用情境</em></p><h2 id="💻-用-OpenCV-驗證產生的-QR-Code"><a href="#💻-用-OpenCV-驗證產生的-QR-Code" class="headerlink" title="💻 用 OpenCV 驗證產生的 QR Code"></a>💻 用 OpenCV 驗證產生的 QR Code</h2><p>產生後立刻用 OpenCV 驗證是否可正常讀取：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># verify_qrcode.py</span></span><br><span class="line"><span class="keyword">import</span> qrcode</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment"># 產生 QR Code</span></span><br><span class="line">data = <span class="string">"驗證測試字串"</span>          <span class="comment"># ← 你要驗證的內容</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用 qrcode 產生 QR Code（PIL Image）</span></span><br><span class="line">qr_pil = qrcode.make(data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># PIL → OpenCV 格式轉換 </span></span><br><span class="line">rgb_array = np.array(qr_pil.convert(<span class="string">"RGB"</span>))        <span class="comment"># PIL Image → numpy array (RGB)</span></span><br><span class="line">img = cv2.cvtColor(rgb_array, cv2.COLOR_RGB2BGR)   <span class="comment"># RGB → BGR (OpenCV 格式)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 驗證 QR Code</span></span><br><span class="line">detector = cv2.QRCodeDetector()</span><br><span class="line">decoded_data, bbox, _ = detector.detectAndDecode(img)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"原始內容：<span class="subst">&#123;data&#125;</span>"</span>)</span><br><span class="line">print(<span class="string">f"解碼結果：<span class="subst">&#123;decoded_data&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> decoded_data == data:</span><br><span class="line">    print(<span class="string">"✅ 驗證成功！產生與解碼一致"</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    print(<span class="string">"❌ 驗證失敗"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 儲存圖片到 output 資料夾</span></span><br><span class="line">output_dir = <span class="string">"output"</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(output_dir):</span><br><span class="line">    os.makedirs(output_dir)</span><br><span class="line"></span><br><span class="line">save_path = os.path.join(output_dir, <span class="string">"qrcode_verify.png"</span>)</span><br><span class="line">cv2.imwrite(save_path, img)</span><br><span class="line">print(<span class="string">f"圖片已儲存至：<span class="subst">&#123;save_path&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 顯示圖片</span></span><br><span class="line">cv2.imshow(<span class="string">"Generated QR Code"</span>, img)</span><br><span class="line">cv2.waitKey(<span class="number">0</span>)</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/qrcode-barcode/05.png" alt="產生 QR Code 後立即用 OpenCV QRCodeDetector 解碼驗證，確認生成與讀取結果一致"><br><em>圖：產生 QR Code 後立即用 OpenCV QRCodeDetector 解碼驗證，確認生成與讀取結果一致</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>圖片品質影響辨識率</strong>：模糊、光線不足、角度過斜都會降低辨識率。實際應用時建議先做影像前處理（灰階、銳化）再解碼。</li><li><strong>pyzbar 比 OpenCV 內建的 QRCodeDetector 更通用</strong>：OpenCV 只能解 QR Code，pyzbar 支援十幾種條碼格式，建議以 pyzbar 為主。</li><li><strong>pyzbar 在 macOS 上需要額外安裝 zbar</strong>：<code>brew install zbar</code>。</li><li><strong>容錯等級越高，QR Code 越複雜</strong>：<code>ERROR_CORRECT_H</code> 雖然耐用，但產生的圖案較密集，距離過遠時反而難以掃描。</li><li><strong>scanned set 防止重複觸發</strong>：即時掃描時同一個條碼可能連續數幀都被偵測到，用 set 記錄已掃描的內容可避免重複處理。</li></ul><h2 id="📊-應用場景"><a href="#📊-應用場景" class="headerlink" title="📊 應用場景"></a>📊 應用場景</h2><ul><li><strong>門票驗證</strong>：掃描入場 QR Code，即時查詢資料庫確認有效性。</li><li><strong>商品盤點</strong>：批次掃描倉庫照片，自動統計 EAN-13 條碼數量。</li><li><strong>名片辨識</strong>：掃描名片上的 QR Code，擷取聯絡資訊。</li><li><strong>產線追蹤</strong>：每個產品貼上 QR Code，在生產各階段掃描記錄流程。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>QR Code 與 BarCode 辨識是電腦視覺中相對容易上手、卻非常實用的一塊。<br>搭配 pyzbar 與 OpenCV，幾十行程式就能完成即時掃描系統。</p><p>下一篇將進入 <a href="/python-opencv-20260413-python-opencv-project-filter-camera"><strong>OpenCV 專案：即時濾鏡相機</strong></a>，實作一個可即時切換、疊加多種視覺效果的濾鏡相機。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​​​‌​​‌‌​​​​‌​‌‌‌​​‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​​‌​​​‌‌​​‌​‌</p><blockquote><p>註：以上參考了<br><a href="https://docs.opencv.org/4.x/de/dc3/classcv_1_1QRCodeDetector.html" target="_blank" rel="external nofollow noopener noreferrer">OpenCV QRCodeDetector 官方文件</a><br><a href="https://github.com/NaturalHistoryMuseum/pyzbar" target="_blank" rel="external nofollow noopener noreferrer">pyzbar GitHub</a><br><a href="https://pypi.org/project/qrcode/" target="_blank" rel="external nofollow noopener noreferrer">qrcode PyPI</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在 &lt;a href=&quot;/python-opencv-20260405-python-opencv-inference
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="08.專案實作篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/08-%E5%B0%88%E6%A1%88%E5%AF%A6%E4%BD%9C%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 常見問題 Q&amp;A</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260411-python-opencv-yolov8-faq/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260411-python-opencv-yolov8-faq/</id>
    <published>2026-04-11T10:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260411-python-opencv-yolov8-inference"><strong>YOLOv8 推論與匯出</strong></a> 中，我們完成了整個 YOLOv8 自訓練物件偵測的流程。</p><p>走完一遍之後，往往會冒出很多實際操作上的問題。本篇整理成 Q&amp;A 格式，針對最常見的問題逐一解答。</p><h2 id="📋-目錄"><a href="#📋-目錄" class="headerlink" title="📋 目錄"></a>📋 目錄</h2><p><strong>增加類別</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><ul><li><a href="#q1">Q1 — 想新增類別，只訓練新類別資料可以嗎？</a></li><li><a href="#q2">Q2 — Fine-tuning 時只放新類別資料就好嗎？</a></li><li><a href="#q3">Q3 — 遷移學習 Fine-tuning 的做法？</a></li><li><a href="#q4">Q4 — 原本的訓練資料遺失了怎麼辦？</a></li><li><a href="#q5">Q5 — 一個類別訓練一個模型，好不好？</a></li></ul><p><strong>資料集</strong></p><ul><li><a href="#q6">Q6 — 每個類別需要幾張圖？</a></li><li><a href="#q7">Q7 — 訓練集和驗證集的比例怎麼分？</a></li><li><a href="#q8">Q8 — 圖片一定要先 resize 成 640×640 嗎？</a></li><li><a href="#q9">Q9 — 資料增強需要自己處理嗎？</a></li></ul><p><strong>訓練</strong></p><ul><li><a href="#q10">Q10 — 訓練中斷了，可以繼續接著訓練嗎？</a></li><li><a href="#q11">Q11 — 沒有顯卡，用 CPU 訓練可以嗎？</a></li><li><a href="#q12">Q12 — yolov8n/s/m/l/x 要選哪個？</a></li></ul><p><strong>效果診斷</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><ul><li><a href="#q13">Q13 — mAP 高但實際效果差，為什麼？</a></li><li><a href="#q14">Q14 — 某個類別 AP 特別低，怎麼改善？</a></li><li><a href="#q15">Q15 — 同一物件被框了好幾個框，怎麼解決？</a></li><li><a href="#q16">Q16 — 小物件偵測效果很差，怎麼辦？</a></li><li><a href="#q17">Q17 — 目標因遠近變大變小，需要特別處理嗎？</a></li></ul><p><strong>標註與模型資源</strong></p><ul><li><a href="#q18">Q18 — 可以用遊戲 SPR 圖檔做預標籤模型嗎？</a></li><li><a href="#q19">Q19 — 已有訓練好的模型，可以幫新圖片自動產生標籤嗎？</a></li><li><a href="#q20">Q20 — 哪裡可以下載別人已訓練好的模型？</a></li></ul><p><a id="q1"></a></p><h2 id="❓-Q1：已訓練好-cat-dog-模型，想新增-fish-類別，可以只訓練-fish-的資料直接加進去嗎？"><a href="#❓-Q1：已訓練好-cat-dog-模型，想新增-fish-類別，可以只訓練-fish-的資料直接加進去嗎？" class="headerlink" title="❓ Q1：已訓練好 cat/dog 模型，想新增 fish 類別，可以只訓練 fish 的資料直接加進去嗎？"></a>❓ Q1：已訓練好 cat/dog 模型，想新增 fish 類別，可以只訓練 fish 的資料直接加進去嗎？</h2><p><strong>A：不行，因為模型的輸出格式在訓練時就已固定，沒辦法事後加欄位。</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><p>可以把它想成一張<strong>印好的表格</strong>：cat/dog 模型只有「2 個類別欄位」，要新增 fish 就得改成「3 個類別欄位」——整張表要重印，沒辦法在旁邊貼一欄上去。</p><p>面對這個情況，有兩種策略：</p><table><thead><tr><th>策略</th><th>是否需要舊資料</th><th>推論速度</th><th>適用場景</th></tr></thead><tbody><tr><td>遷移學習 Fine-tuning（推薦）</td><td>需要全部</td><td>快（單模型）</td><td>有舊資料、追求精準</td></tr><tr><td>獨立模型 + 合併推論</td><td>只需新類別</td><td>慢（多模型）</td><td>舊資料遺失、模組化需求</td></tr></tbody></table><p><a id="q2"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q2：Fine-tuning-時，只放-fish-的資料就好了嗎？"><a href="#❓-Q2：Fine-tuning-時，只放-fish-的資料就好了嗎？" class="headerlink" title="❓ Q2：Fine-tuning 時，只放 fish 的資料就好了嗎？"></a>❓ Q2：Fine-tuning 時，只放 fish 的資料就好了嗎？</h2><p><strong>A：不行，模型會「忘掉」cat 和 dog。</strong></p><p>這個現象叫做<strong>災難性遺忘（Catastrophic Forgetting）</strong>：模型只看 fish 資料時，原本辨識 cat/dog 的記憶會被覆蓋，訓練完就只認識 fish 了。</p><p><strong>Fine-tuning 時，資料集必須同時包含 cat、dog、fish 三個類別的圖片。</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><p><a id="q3"></a></p><h2 id="❓-Q3：遷移學習-Fine-tuning-的做法？"><a href="#❓-Q3：遷移學習-Fine-tuning-的做法？" class="headerlink" title="❓ Q3：遷移學習 Fine-tuning 的做法？"></a>❓ Q3：遷移學習 Fine-tuning 的做法？</h2><p><strong>步驟：</strong></p><ol><li>準備 fish 的圖片與標籤</li><li>把 cat/dog 原有資料與 fish 資料合併</li><li>更新 <code>data.yaml</code>，<strong>類別順序不能改</strong>（cat=0, dog=1 維持不動）</li><li>從 <code>best.pt</code> 繼續訓練</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># data_v2.yaml</span></span><br><span class="line"><span class="attr">path:</span> <span class="string">.</span></span><br><span class="line"><span class="attr">train:</span> <span class="string">train_v2.txt</span>     <span class="comment"># cat + dog + fish 的訓練圖片路徑</span></span><br><span class="line"><span class="attr">val:</span>   <span class="string">val_v2.txt</span>       <span class="comment"># cat + dog + fish 的驗證圖片路徑</span></span><br><span class="line"></span><br><span class="line"><span class="attr">nc:</span> <span class="number">3</span></span><br><span class="line"><span class="attr">names:</span> <span class="string">['cat',</span> <span class="string">'dog'</span><span class="string">,</span> <span class="string">'fish'</span><span class="string">]</span>   <span class="comment"># 舊順序不能改！新類別接在後面</span></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># finetune_add_class.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="comment"># 類別數改變時，偵測頭會自動重建；主幹（Backbone）特徵保留</span></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data_v2.yaml"</span>,</span><br><span class="line">    epochs=<span class="number">100</span>,</span><br><span class="line">    imgsz=<span class="number">640</span>,</span><br><span class="line">    batch=<span class="number">16</span>,</span><br><span class="line">    name=<span class="string">"custom_detector_v2"</span>,</span><br><span class="line">    lr0=<span class="number">0.001</span>,      <span class="comment"># 遷移學習建議用較小的起始學習率</span></span><br><span class="line">    lrf=<span class="number">0.01</span>,</span><br><span class="line">    patience=<span class="number">20</span>,</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><a id="q4"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q4：原本的-cat-dog-訓練資料遺失了怎麼辦？"><a href="#❓-Q4：原本的-cat-dog-訓練資料遺失了怎麼辦？" class="headerlink" title="❓ Q4：原本的 cat/dog 訓練資料遺失了怎麼辦？"></a>❓ Q4：原本的 cat/dog 訓練資料遺失了怎麼辦？</h2><p><strong>A：改用「獨立模型 + 合併推論」，只需準備新類別的資料。</strong></p><p>先單獨訓練一個 fish 模型：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># train_fish.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data_fish.yaml"</span>,   <span class="comment"># nc: 1, names: ['fish']</span></span><br><span class="line">    epochs=<span class="number">100</span>,</span><br><span class="line">    imgsz=<span class="number">640</span>,</span><br><span class="line">    batch=<span class="number">16</span>,</span><br><span class="line">    name=<span class="string">"fish_detector"</span>,</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>推論時兩個模型各跑一次，手動合併結果：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_merged.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">model_cat_dog = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)   <span class="comment"># cat=0, dog=1</span></span><br><span class="line">model_fish    = YOLO(<span class="string">"runs/detect/fish_detector/weights/best.pt"</span>)      <span class="comment"># fish=0</span></span><br><span class="line"></span><br><span class="line">CLASS_NAMES = [<span class="string">"cat"</span>, <span class="string">"dog"</span>, <span class="string">"fish"</span>]</span><br><span class="line"></span><br><span class="line">img = cv2.imread(<span class="string">"assets/test.jpg"</span>)</span><br><span class="line">detections = []</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> box <span class="keyword">in</span> model_cat_dog(img, conf=<span class="number">0.5</span>, verbose=<span class="literal">False</span>)[<span class="number">0</span>].boxes:</span><br><span class="line">    cls_id = int(box.cls)           <span class="comment"># 0=cat, 1=dog，不需偏移</span></span><br><span class="line">    detections.append((cls_id, float(box.conf), list(map(int, box.xyxy[<span class="number">0</span>]))))</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> box <span class="keyword">in</span> model_fish(img, conf=<span class="number">0.5</span>, verbose=<span class="literal">False</span>)[<span class="number">0</span>].boxes:</span><br><span class="line">    cls_id = int(box.cls) + <span class="number">2</span>       <span class="comment"># fish=0 → 合併表中為 2</span></span><br><span class="line">    detections.append((cls_id, float(box.conf), list(map(int, box.xyxy[<span class="number">0</span>]))))</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> cls_id, conf, (x1, y1, x2, y2) <span class="keyword">in</span> detections:</span><br><span class="line">    label = <span class="string">f"<span class="subst">&#123;CLASS_NAMES[cls_id]&#125;</span>: <span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span></span><br><span class="line">    cv2.rectangle(img, (x1, y1), (x2, y2), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">    cv2.putText(img, label, (x1, y1 - <span class="number">8</span>),</span><br><span class="line">                cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.6</span>, (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>)</span><br><span class="line">    print(label)</span><br><span class="line"></span><br><span class="line">cv2.imwrite(<span class="string">"output/result_merged.jpg"</span>, img)</span><br><span class="line">print(<span class="string">"已儲存到 output/result_merged.jpg"</span>)</span><br></pre></td></tr></table></figure><p><a id="q5"></a></p><h2 id="❓-Q5：一個類別訓練一個模型，好不好？"><a href="#❓-Q5：一個類別訓練一個模型，好不好？" class="headerlink" title="❓ Q5：一個類別訓練一個模型，好不好？"></a>❓ Q5：一個類別訓練一個模型，好不好？</h2><p><strong>A：可行，但長期維護成本高，不建議作為主力做法。</strong></p><table><thead><tr><th>面向</th><th>獨立模型</th><th>合併模型（Fine-tuning）</th></tr></thead><tbody><tr><td>新增類別</td><td>只需訓練新類別</td><td>需要全部資料重訓</td></tr><tr><td>推論速度</td><td>慢（N 個模型各跑一次）</td><td>快（單次推論）</td></tr><tr><td>記憶體用量</td><td>高（同時載入多個模型）</td><td>低</td></tr><tr><td>class index 管理</td><td>需手動對齊</td><td>自動處理</td></tr></tbody></table><p><a id="q6"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q6：每個類別需要幾張圖？有最低門檻嗎？"><a href="#❓-Q6：每個類別需要幾張圖？有最低門檻嗎？" class="headerlink" title="❓ Q6：每個類別需要幾張圖？有最低門檻嗎？"></a>❓ Q6：每個類別需要幾張圖？有最低門檻嗎？</h2><p><strong>A：沒有硬性規定，但有個參考範圍。</strong></p><table><thead><tr><th>資料量</th><th>效果</th></tr></thead><tbody><tr><td>&lt; 50 張</td><td>容易過擬合，難以泛化</td></tr><tr><td>100～300 張</td><td>最低門檻，類別簡單時勉強夠用</td></tr><tr><td>300～500 張</td><td>效果開始穩定</td></tr><tr><td>1000 張以上</td><td>大多數場景都能有不錯表現</td></tr></tbody></table><blockquote><p>💡 <strong>比張數更重要的是「多樣性」</strong>：不同場景、不同光線、不同角度各拍 100 張，效果通常優於同一場景拍 1000 張。</p></blockquote><p><a id="q7"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q7：訓練集和驗證集的比例怎麼分？"><a href="#❓-Q7：訓練集和驗證集的比例怎麼分？" class="headerlink" title="❓ Q7：訓練集和驗證集的比例怎麼分？"></a>❓ Q7：訓練集和驗證集的比例怎麼分？</h2><p><strong>A：常用 8:2，資料量少時可用 7:3。</strong></p><table><thead><tr><th>資料總量</th><th>建議比例</th></tr></thead><tbody><tr><td>&lt; 500 張</td><td>7:3</td></tr><tr><td>500～2000 張</td><td>8:2（最常用）</td></tr><tr><td>&gt; 2000 張</td><td>9:1</td></tr></tbody></table><blockquote><p>⚠️ 驗證集的分布要和訓練集相似，不能把所有「困難」的圖全放進驗證集，否則驗證指標會失真。</p></blockquote><p><a id="q8"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q8：圖片一定要先-resize-成-640×640-嗎？"><a href="#❓-Q8：圖片一定要先-resize-成-640×640-嗎？" class="headerlink" title="❓ Q8：圖片一定要先 resize 成 640×640 嗎？"></a>❓ Q8：圖片一定要先 resize 成 640×640 嗎？</h2><p><strong>A：不需要，YOLOv8 訓練時會自動處理。</strong></p><p>原始圖片可以是任意解析度，框架會自動縮放並補黑邊（letterbox）維持長寬比。若圖片中有很小的目標，可以提高 <code>imgsz=1280</code> 保留更多細節，但訓練速度會變慢。</p><p><a id="q9"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q9：資料增強需要自己處理嗎？YOLOv8-有內建嗎？"><a href="#❓-Q9：資料增強需要自己處理嗎？YOLOv8-有內建嗎？" class="headerlink" title="❓ Q9：資料增強需要自己處理嗎？YOLOv8 有內建嗎？"></a>❓ Q9：資料增強需要自己處理嗎？YOLOv8 有內建嗎？</h2><p><strong>A：不需要，YOLOv8 訓練時預設開啟多種增強。</strong></p><table><thead><tr><th>增強方式</th><th>說明</th></tr></thead><tbody><tr><td>Mosaic</td><td>把 4 張圖拼在一起，增加場景複雜度</td></tr><tr><td>隨機縮放、平移、裁切</td><td>讓模型看到不同大小和位置的目標</td></tr><tr><td>水平翻轉</td><td>預設開啟</td></tr><tr><td>HSV 色彩抖動</td><td>隨機調整色調、飽和度、亮度</td></tr><tr><td>Copy-paste</td><td>把物件複製貼到其他圖片上</td></tr></tbody></table><p>一般不需要自己處理，若想調整強度，可修改訓練參數中的 <code>mosaic</code>、<code>flipud</code>、<code>fliplr</code> 等。</p><p><a id="q10"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q10：訓練到一半中斷了（停電、當機），可以繼續接著訓練嗎？"><a href="#❓-Q10：訓練到一半中斷了（停電、當機），可以繼續接著訓練嗎？" class="headerlink" title="❓ Q10：訓練到一半中斷了（停電、當機），可以繼續接著訓練嗎？"></a>❓ Q10：訓練到一半中斷了（停電、當機），可以繼續接著訓練嗎？</h2><p><strong>A：可以，用 <code>resume=True</code> 從中斷點繼續。</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># resume_training.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用 last.pt（不是 best.pt），記錄了目前訓練進度</span></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/last.pt"</span>)</span><br><span class="line">model.train(resume=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure><blockquote><p>💡 <code>resume=True</code> 會自動恢復所有訓練設定，不需要重新指定 epochs 或其他參數。</p></blockquote><p><a id="q11"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q11：沒有顯卡，用-CPU-訓練可以嗎？速度差多少？"><a href="#❓-Q11：沒有顯卡，用-CPU-訓練可以嗎？速度差多少？" class="headerlink" title="❓ Q11：沒有顯卡，用 CPU 訓練可以嗎？速度差多少？"></a>❓ Q11：沒有顯卡，用 CPU 訓練可以嗎？速度差多少？</h2><p><strong>A：可以執行，但速度非常慢。</strong></p><ul><li>CPU 比 GPU 慢 <strong>10～50 倍</strong></li><li>資料量少（100 張以下）勉強可接受</li><li>資料量較大時，建議改用 <strong>Google Colab</strong>（免費 T4 GPU）</li></ul><p><a id="q12"></a></p><h2 id="❓-Q12：yolov8n-yolov8s-yolov8m-yolov8l-yolov8x，要選哪個？"><a href="#❓-Q12：yolov8n-yolov8s-yolov8m-yolov8l-yolov8x，要選哪個？" class="headerlink" title="❓ Q12：yolov8n / yolov8s / yolov8m / yolov8l / yolov8x，要選哪個？"></a>❓ Q12：yolov8n / yolov8s / yolov8m / yolov8l / yolov8x，要選哪個？</h2><p><strong>A：依速度和精準度的需求取捨。</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><table><thead><tr><th>模型</th><th>推論速度</th><th>精準度</th><th>實際案例</th></tr></thead><tbody><tr><td>yolov8n</td><td>最快</td><td>最低</td><td>智慧門鈴偵測人臉、無人機即時避障</td></tr><tr><td>yolov8s</td><td>快</td><td>普通</td><td>手機 App 掃描商品、樹莓派動作偵測</td></tr><tr><td>yolov8m</td><td>中等</td><td>良好</td><td>安全監控客流分析、<strong>遊戲畫面人物／物品／怪物偵測</strong></td></tr><tr><td>yolov8l</td><td>慢</td><td>高</td><td>工廠生產線瑕疵檢測、倉庫自動盤點</td></tr><tr><td>yolov8x</td><td>最慢</td><td>最高</td><td>衛星影像分析、醫療影像輔助診斷</td></tr></tbody></table><p><strong>遊戲畫面偵測落在 yolov8s～yolov8m 之間：</strong></p><ul><li>動作 RPG、FPS 等節奏快 → <strong>yolov8s</strong>（偵測必須跟上畫面）</li><li>回合制、策略遊戲等節奏慢 → <strong>yolov8m</strong>（精準度更重要）</li></ul><blockquote><p>💡 建議先用 <code>yolov8n</code> 或 <code>yolov8s</code> 跑通整個流程，再換較大的模型測試精準度是否有明顯提升。</p></blockquote><p><a id="q13"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q13：mAP-很高，但實際場景測試效果很差，為什麼？"><a href="#❓-Q13：mAP-很高，但實際場景測試效果很差，為什麼？" class="headerlink" title="❓ Q13：mAP 很高，但實際場景測試效果很差，為什麼？"></a>❓ Q13：mAP 很高，但實際場景測試效果很差，為什麼？</h2><p><strong>A：通常是訓練資料和實際場景「差太多」造成的。</strong></p><ul><li>驗證集圖片太「標準」，但實際有複雜背景、不同光線、奇怪角度</li><li>模型過擬合，只記住了訓練資料的特徵</li><li>實際場景的目標尺寸或角度從未出現在訓練資料中</li></ul><p><strong>解法：把實際場景拍攝的圖片加入訓練集。</strong></p><p><a id="q14"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q14：某個類別的-AP-特別低，怎麼改善？"><a href="#❓-Q14：某個類別的-AP-特別低，怎麼改善？" class="headerlink" title="❓ Q14：某個類別的 AP 特別低，怎麼改善？"></a>❓ Q14：某個類別的 AP 特別低，怎麼改善？</h2><p><strong>A：先看 <code>confusion_matrix</code> 找出根本原因，再對症下藥。</strong></p><table><thead><tr><th>可能原因</th><th>解法</th></tr></thead><tbody><tr><td>該類別訓練資料太少</td><td>補充更多圖片</td></tr><tr><td>和其他類別外觀相似，常被誤認</td><td>補充更多對比性樣本</td></tr><tr><td>標籤品質差（框標歪、標錯）</td><td>重新檢查並修正標籤</td></tr><tr><td>該類別在畫面中通常很小</td><td>參考 Q16</td></tr></tbody></table><p><a id="q15"></a></p><h2 id="❓-Q15：同一個物件被框了好幾個框（重複偵測），怎麼解決？"><a href="#❓-Q15：同一個物件被框了好幾個框（重複偵測），怎麼解決？" class="headerlink" title="❓ Q15：同一個物件被框了好幾個框（重複偵測），怎麼解決？"></a>❓ Q15：同一個物件被框了好幾個框（重複偵測），怎麼解決？</h2><p><strong>A：調低 <code>iou</code> 閾值，讓過濾更積極。</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><p><code>iou</code> 是兩個框的重疊程度（0 = 完全不重疊，1 = 完全重疊）。預設 <code>iou=0.7</code> 表示「重疊超過 70% 才算重複並過濾」。若重複框明顯，往下調讓過濾條件更寬鬆：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_nms.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># iou 調低 → 過濾更積極，重複框減少</span></span><br><span class="line">results = model(<span class="string">"assets/test.jpg"</span>, conf=<span class="number">0.5</span>, iou=<span class="number">0.45</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> box <span class="keyword">in</span> results[<span class="number">0</span>].boxes:</span><br><span class="line">    cls_id = int(box.cls)</span><br><span class="line">    conf   = float(box.conf)</span><br><span class="line">    print(<span class="string">f"<span class="subst">&#123;model.names[cls_id]&#125;</span>: <span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 物件本身密集（如人群、排列貨物）時，不要調太低，否則會漏掉真實物件。</p></blockquote><p><a id="q16"></a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><h2 id="❓-Q16：小物件偵測效果很差，怎麼辦？"><a href="#❓-Q16：小物件偵測效果很差，怎麼辦？" class="headerlink" title="❓ Q16：小物件偵測效果很差，怎麼辦？"></a>❓ Q16：小物件偵測效果很差，怎麼辦？</h2><p><strong>A：有幾個方向可以嘗試。</strong></p><table><thead><tr><th>方法</th><th>說明</th></tr></thead><tbody><tr><td>提高 <code>imgsz</code></td><td>640 → 1280，保留更多細節（訓練更慢、更吃記憶體）</td></tr><tr><td>降低 <code>conf</code> 閾值</td><td>小物件信心度通常較低，試試 <code>conf=0.3</code></td></tr><tr><td>補充小物件樣本</td><td>訓練集裡沒有小目標的圖，模型根本沒學過怎麼偵測</td></tr><tr><td>切片推論（SAHI）</td><td>把大圖切成小塊分別推論再合併，適合航拍、監控場景</td></tr></tbody></table><p><a id="q17"></a></p><h2 id="❓-Q17：目標會因為遠近變大變小，訓練需要特別處理嗎？YOLOv8-怎麼實現的？"><a href="#❓-Q17：目標會因為遠近變大變小，訓練需要特別處理嗎？YOLOv8-怎麼實現的？" class="headerlink" title="❓ Q17：目標會因為遠近變大變小，訓練需要特別處理嗎？YOLOv8 怎麼實現的？"></a>❓ Q17：目標會因為遠近變大變小，訓練需要特別處理嗎？YOLOv8 怎麼實現的？</h2><p><strong>A：不需要，YOLOv8 天生就會同時偵測不同大小的目標。</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><p>YOLOv8 在同一次推論中，同時用三個不同的「偵測層」各自負責不同大小的目標：</p><table><thead><tr><th>偵測層</th><th>負責偵測</th><th>對應場景</th></tr></thead><tbody><tr><td>細節層</td><td>小物件</td><td>遠距離目標、畫面中的小物件</td></tr><tr><td>中間層</td><td>中型物件</td><td>一般距離目標</td></tr><tr><td>全局層</td><td>大物件</td><td>近距離目標</td></tr></tbody></table><p>三層同時運作，不論目標遠近大小，都有對應的層負責，<strong>不需要做任何特別處理</strong>。</p><blockquote><p>💡 唯一要注意：<strong>訓練資料中要有遠中近各種距離的樣本</strong>，確保三層都有學習機會。若遠距效果差，通常是遠距樣本太少，補充即可。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p></blockquote><p><a id="q18"></a></p><h2 id="❓-Q18：可以用遊戲的-SPR-圖檔先訓練預標籤模型，再拿來自動標記遊戲截圖嗎？"><a href="#❓-Q18：可以用遊戲的-SPR-圖檔先訓練預標籤模型，再拿來自動標記遊戲截圖嗎？" class="headerlink" title="❓ Q18：可以用遊戲的 SPR 圖檔先訓練預標籤模型，再拿來自動標記遊戲截圖嗎？"></a>❓ Q18：可以用遊戲的 SPR 圖檔先訓練預標籤模型，再拿來自動標記遊戲截圖嗎？</h2><p><strong>A：可以，這是有效的兩階段標註策略。</strong></p><table><thead><tr><th>階段</th><th>做什麼</th></tr></thead><tbody><tr><td>① 訓練預標籤模型</td><td>用 SPR 圖訓練一個粗糙的偵測器</td></tr><tr><td>② 自動標記截圖</td><td>用預標籤模型對遊戲截圖做自動框選</td></tr><tr><td>③ 人工校正</td><td>用 LabelImg 檢查並修正框選結果</td></tr><tr><td>④ 訓練最終模型</td><td>用校正後的截圖訓練正式模型</td></tr></tbody></table><p><strong>讓預標籤更準的技巧</strong>：SPR 圖通常是透明或純色背景，可以先把 SPR 圖合成到遊戲背景截圖上再訓練，讓模型提前學會在複雜背景中辨識目標。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><ul><li><strong>2D 遊戲</strong>：SPR 和遊戲畫面幾乎一致，效果最好</li><li><strong>3D 遊戲</strong>：有光影和透視差異，準確率較低，但仍比從頭手標省力</li><li>無論哪種，<strong>校正步驟不能省略</strong></li></ul><p><a id="q19"></a></p><h2 id="❓-Q19：我已經有一個訓練好的模型，可以直接用它來幫新圖片產生標籤嗎？"><a href="#❓-Q19：我已經有一個訓練好的模型，可以直接用它來幫新圖片產生標籤嗎？" class="headerlink" title="❓ Q19：我已經有一個訓練好的模型，可以直接用它來幫新圖片產生標籤嗎？"></a>❓ Q19：我已經有一個訓練好的模型，可以直接用它來幫新圖片產生標籤嗎？</h2><p><strong>A：可以，而且比 Q18 的 SPR 方式更準。</strong></p><p>讓模型對新圖片推論，把結果輸出成 YOLO 格式的 <code>.txt</code> 標籤檔，再用 LabelImg 校正即可。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># pre_label.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">model     = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line">img_dir   = <span class="string">"assets/new_images/"</span></span><br><span class="line">label_dir = <span class="string">"assets/new_labels/"</span></span><br><span class="line">os.makedirs(label_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> img_file <span class="keyword">in</span> os.listdir(img_dir):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> img_file.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".png"</span>)):</span><br><span class="line">        <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">    results = model(os.path.join(img_dir, img_file), conf=<span class="number">0.3</span>)  <span class="comment"># conf 低一點，寧可多框也不漏框</span></span><br><span class="line">    result  = results[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">    label_path = os.path.join(label_dir, os.path.splitext(img_file)[<span class="number">0</span>] + <span class="string">".txt"</span>)</span><br><span class="line">    <span class="keyword">with</span> open(label_path, <span class="string">"w"</span>) <span class="keyword">as</span> f:</span><br><span class="line">        <span class="keyword">for</span> box <span class="keyword">in</span> result.boxes:</span><br><span class="line">            cls_id        = int(box.cls)</span><br><span class="line">            cx, cy, bw, bh = box.xywhn[<span class="number">0</span>].tolist()   <span class="comment"># 歸一化的中心座標 + 寬高</span></span><br><span class="line">            f.write(<span class="string">f"<span class="subst">&#123;cls_id&#125;</span> <span class="subst">&#123;cx:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;cy:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;bw:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;bh:<span class="number">.6</span>f&#125;</span>\n"</span>)</span><br><span class="line"></span><br><span class="line">    print(<span class="string">f"<span class="subst">&#123;img_file&#125;</span>：<span class="subst">&#123;len(result.boxes)&#125;</span> 個框 → <span class="subst">&#123;label_path&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"完成！請用 LabelImg 開啟資料夾校正。"</span>)</span><br></pre></td></tr></table></figure><ul><li><strong><code>conf=0.3</code></strong>：寧可多框（刪框快）也不漏框（補框慢）</li><li><strong>校正完加回訓練集重新訓練</strong>，下一輪預標籤會更準，工作量越來越少（這就是 <strong>Active Learning</strong>）</li></ul><p><a id="q20"></a></p><h2 id="❓-Q20：有哪些地方可以下載別人已經訓練好的模型？遊戲的也有嗎？"><a href="#❓-Q20：有哪些地方可以下載別人已經訓練好的模型？遊戲的也有嗎？" class="headerlink" title="❓ Q20：有哪些地方可以下載別人已經訓練好的模型？遊戲的也有嗎？"></a>❓ Q20：有哪些地方可以下載別人已經訓練好的模型？遊戲的也有嗎？</h2><p><strong>A：有幾個主要平台，遊戲相關的模型在社群平台上也找得到。</strong></p><table><thead><tr><th>平台</th><th>特色</th><th>適合找什麼</th></tr></thead><tbody><tr><td><a href="https://universe.roboflow.com/" target="_blank" rel="external nofollow noopener noreferrer">Roboflow Universe</a></td><td>最大的公開模型與資料集社群，可直接下載權重</td><td><strong>遊戲偵測模型</strong>、各種自訂類別</td></tr><tr><td><a href="https://hub.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics Hub</a></td><td>YOLOv8 官方平台，品質較有保障</td><td>官方範例與社群分享模型</td></tr><tr><td><a href="https://huggingface.co/" target="_blank" rel="external nofollow noopener noreferrer">Hugging Face</a></td><td>大型 AI 模型託管平台，搜尋 <code>yolov8</code></td><td>各種領域的 YOLOv8 模型</td></tr><tr><td>GitHub</td><td>搜尋 <code>yolov8 + 遊戲名稱</code>（如 <code>yolov8 minecraft</code>）</td><td>針對特定遊戲的個人專案</td></tr></tbody></table><p><strong>找遊戲模型的搜尋建議：</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><ul><li>Roboflow Universe：直接搜尋遊戲名稱，如 <code>league of legends</code>、<code>minecraft</code>、<code>genshin</code></li><li>GitHub：搜尋 <code>yolov8 [遊戲名] detection</code></li></ul><p><strong>下載後先確認：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 載入別人的模型，先查看它認識哪些類別</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line">model = YOLO(<span class="string">"下載的模型.pt"</span>)</span><br><span class="line">print(model.names)</span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 使用前注意：</p><ul><li><strong>確認類別名稱</strong>：用 <code>model.names</code> 查看這個模型認識哪些類別</li><li><strong>確認授權條款</strong>：部分模型有使用限制，商業使用前需確認 License</li><li><strong>品質不保證</strong>：社群模型品質參差不齊，用於預標籤前建議先測幾張圖</li></ul></blockquote><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>遇到問題時，通常可以先從<strong>資料</strong>下手——是否夠多、夠多樣、標籤是否正確。大多數時候，資料品質才是影響模型表現的關鍵。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p><p>下一篇進入新章節 <a href="/python-opencv-20260412-python-opencv-qrcode-barcod"><strong>Python | OpenCV QR Code 與 BarCode 辨識</strong></a>，開始 08.專案實作篇的內容。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/train/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Train</a><br><a href="https://docs.ultralytics.com/guides/transfer-learning-with-yolov8/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Transfer Learning</a><br><a href="https://docs.ultralytics.com/guides/model-training-tips/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Tips for Best Training Results</a><br><a href="https://en.wikipedia.org/wiki/Catastrophic_interference" target="_blank" rel="external nofollow noopener noreferrer">Catastrophic Forgetting — Wikipedia</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌‌​​‌‌​​​​‌​‌‌‌​​​‌</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260411-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 推論與匯出</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260411-python-opencv-yolov8-inference/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260411-python-opencv-yolov8-inference/</id>
    <published>2026-04-11T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260410-python-opencv-yolov8-results"><strong>YOLOv8 訓練結果分析</strong></a> 中，我們確認了模型的訓練品質。<br>恭喜你！已經完成從資料集準備、預標籤、訓練，到結果分析的完整流程。</p><p>現在來到最後一步 —— <strong>把訓練好的模型實際應用起來</strong>。</p><p>這一篇會教你：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><ul><li>如何對單張圖片、影片、即時攝影機進行推論</li><li>如何自訂繪製偵測結果</li><li>如何把模型匯出成 ONNX 格式（方便後續與 OpenCV 深度整合）</li></ul><p>本篇所有範例都會用到前一篇訓練好的最佳模型：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">runs&#x2F;detect&#x2F;custom_detector&#x2F;weights&#x2F;best.pt</span><br></pre></td></tr></table></figure><p>若檔案不存在，請先確認 <a href="/python-opencv-20260409-python-opencv-yolov8-training-advanced"><strong>YOLOv8 訓練進階設定</strong></a> 的 <code>train_full.py</code> 是否正常跑完。</p><h2 id="🎨-範例圖片與影片"><a href="#🎨-範例圖片與影片" class="headerlink" title="🎨 範例圖片與影片"></a>🎨 範例圖片與影片</h2><p><strong>圖片</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><ul><li>來源：<a href="https://www.pexels.com/search/cat/" target="_blank" rel="external nofollow noopener noreferrer">Cat Image</a>。</li><li>下載後將檔名改為 <code>test_cat.jpg</code>，放在 <code>assets/</code>，即可用於以下各範例。</li></ul><p><strong>影片</strong></p><ul><li>來源：<a href="https://www.pexels.com/video/three-dogs-and-a-cat-lying-on-a-sofa-14615296/" target="_blank" rel="external nofollow noopener noreferrer">Cat And Dogs Video</a>。</li><li>下載後將檔名改為 <code>test_cat_and_dog.mp4</code>，放在 <code>assets/</code>，即可用於影片推論範例。</li></ul><h2 id="💻-靜態圖片推論"><a href="#💻-靜態圖片推論" class="headerlink" title="💻 靜態圖片推論"></a>💻 靜態圖片推論</h2><p><strong>說明</strong>：<br><code>conf=0.5</code> 是信心度門檻，建議從 0.5 開始調整，太低會有很多誤報，太高會漏掉物件。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_image.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 推論</span></span><br><span class="line">results = model(<span class="string">"assets/test_cat.jpg"</span>, conf=<span class="number">0.5</span>, save=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 取得第一張結果（單張圖片通常只有一個）</span></span><br><span class="line">result = results[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 印出偵測資訊</span></span><br><span class="line"><span class="keyword">for</span> box <span class="keyword">in</span> result.boxes:</span><br><span class="line">    cls_id = int(box.cls)</span><br><span class="line">    conf = float(box.conf)</span><br><span class="line">    x1, y1, x2, y2 = map(int, box.xyxy[<span class="number">0</span>])</span><br><span class="line">    label = <span class="string">f"<span class="subst">&#123;model.names[cls_id]&#125;</span>: <span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span></span><br><span class="line">    print(<span class="string">f"偵測到：<span class="subst">&#123;label&#125;</span>，座標：(<span class="subst">&#123;x1&#125;</span>,<span class="subst">&#123;y1&#125;</span>) - (<span class="subst">&#123;x2&#125;</span>,<span class="subst">&#123;y2&#125;</span>)"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># ========== 顯示圖片（推薦寫法） ==========</span></span><br><span class="line">img = result.plot()                    <span class="comment"># 產生帶框的圖片 (BGR格式)</span></span><br><span class="line">cv2.imshow(<span class="string">"YOLOv8 Detection"</span>, img)    <span class="comment"># 自訂視窗名稱</span></span><br><span class="line">cv2.waitKey(<span class="number">0</span>)                         <span class="comment"># 按任意鍵關閉視窗</span></span><br><span class="line">cv2.destroyAllWindows()                <span class="comment"># 關閉所有 OpenCV 視窗</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 儲存結果</span></span><br><span class="line">result.save(filename=<span class="string">"output/result.jpg"</span>)</span><br><span class="line">print(<span class="string">"已儲存到 output/result.jpg"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-inference/01.png" alt="載入訓練好的 YOLOv8 模型對靜態圖片推論，取得邊界框座標、類別與信心度並儲存結果"><br><em>圖：載入訓練好的 YOLOv8 模型對靜態圖片推論，取得邊界框座標、類別與信心度並儲存結果</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><h2 id="💻-批次圖片推論"><a href="#💻-批次圖片推論" class="headerlink" title="💻 批次圖片推論"></a>💻 批次圖片推論</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_batch.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">model   = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line">img_dir = <span class="string">"assets/"</span></span><br><span class="line">out_dir = <span class="string">"output/"</span>                         <span class="comment"># 輸出目錄：output/</span></span><br><span class="line">os.makedirs(out_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">image_paths = [os.path.join(img_dir, f)</span><br><span class="line">               <span class="keyword">for</span> f <span class="keyword">in</span> os.listdir(img_dir)</span><br><span class="line">               <span class="keyword">if</span> f.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".png"</span>))]</span><br><span class="line"></span><br><span class="line">results = model(image_paths, conf=<span class="number">0.5</span>, batch=<span class="number">8</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i, r <span class="keyword">in</span> enumerate(results):</span><br><span class="line">    print(<span class="string">f"\n圖片 <span class="subst">&#123;image_paths[i]&#125;</span>："</span>)</span><br><span class="line">    <span class="keyword">for</span> box <span class="keyword">in</span> r.boxes:</span><br><span class="line">        cls_id = int(box.cls)</span><br><span class="line">        conf   = float(box.conf)</span><br><span class="line">        print(<span class="string">f"  <span class="subst">&#123;model.names[cls_id]&#125;</span>: <span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    fname = os.path.basename(image_paths[i])</span><br><span class="line">    r.save(filename=os.path.join(out_dir, fname))  <span class="comment"># 保留原檔名，存入 output/</span></span><br><span class="line"></span><br><span class="line">print(<span class="string">f"\n完成：結果儲存至 <span class="subst">&#123;out_dir&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-inference/02.gif" alt="批次讀取目錄中的所有圖片一次送入模型推論，輸出各圖片的偵測類別並儲存標註結果"><br><em>圖：批次讀取目錄中的所有圖片一次送入模型推論，輸出各圖片的偵測類別並儲存標註結果</em></p><h2 id="💻-影片推論"><a href="#💻-影片推論" class="headerlink" title="💻 影片推論"></a>💻 影片推論</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_video.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 對影片推論，結果自動儲存</span></span><br><span class="line">results = model(</span><br><span class="line">    <span class="string">"assets/test_cat_and_dog.mp4"</span>,</span><br><span class="line">    conf=<span class="number">0.5</span>,</span><br><span class="line">    save=<span class="literal">True</span>,          <span class="comment"># 儲存標註後的影片，預設輸出在 runs/detect/predict/</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-inference/03.gif" alt="對影片檔案執行 YOLOv8 推論，自動儲存標註後的輸出影片"><br><em>圖：對影片檔案執行 YOLOv8 推論，自動儲存標註後的輸出影片</em></p><h2 id="💻-即時攝影機推論"><a href="#💻-即時攝影機推論" class="headerlink" title="💻 即時攝影機推論"></a>💻 即時攝影機推論</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_camera.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line">print(<span class="string">"按 q 離開"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    results = model(frame, conf=<span class="number">0.5</span>, verbose=<span class="literal">False</span>)</span><br><span class="line">    annotated = results[<span class="number">0</span>].plot()</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"YOLOv8 Detection"</span>, annotated)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><blockquote><p>📝 註：由於本身沒有攝影鏡頭，所以無法示範效果。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p></blockquote><h2 id="💻-手動繪製偵測結果（自訂樣式）"><a href="#💻-手動繪製偵測結果（自訂樣式）" class="headerlink" title="💻 手動繪製偵測結果（自訂樣式）"></a>💻 手動繪製偵測結果（自訂樣式）</h2><p>若需要自訂邊界框顏色或字型，可不使用 <code>.plot()</code> 而自行繪製：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_camera_custom.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line">COLORS = [(<span class="number">0</span>,<span class="number">255</span>,<span class="number">0</span>), (<span class="number">255</span>,<span class="number">0</span>,<span class="number">0</span>), (<span class="number">0</span>,<span class="number">0</span>,<span class="number">255</span>), (<span class="number">255</span>,<span class="number">255</span>,<span class="number">0</span>)]  <span class="comment"># 每個類別一個顏色</span></span><br><span class="line"></span><br><span class="line">cap = cv2.VideoCapture(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">    ret, frame = cap.read()</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ret:</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    results = model(frame, conf=<span class="number">0.5</span>, verbose=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> box <span class="keyword">in</span> results[<span class="number">0</span>].boxes:</span><br><span class="line">        cls_id      = int(box.cls)</span><br><span class="line">        conf        = float(box.conf)</span><br><span class="line">        x1,y1,x2,y2 = map(int, box.xyxy[<span class="number">0</span>])</span><br><span class="line">        color       = COLORS[cls_id % len(COLORS)]</span><br><span class="line">        label       = <span class="string">f"<span class="subst">&#123;model.names[cls_id]&#125;</span> <span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span></span><br><span class="line"></span><br><span class="line">        cv2.rectangle(frame, (x1, y1), (x2, y2), color, <span class="number">2</span>)</span><br><span class="line">        cv2.putText(frame, label, (x1, y1 - <span class="number">8</span>),</span><br><span class="line">                    cv2.FONT_HERSHEY_SIMPLEX, <span class="number">0.6</span>, color, <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line">    cv2.imshow(<span class="string">"YOLOv8"</span>, frame)</span><br><span class="line">    <span class="keyword">if</span> cv2.waitKey(<span class="number">1</span>) &amp; <span class="number">0xFF</span> == ord(<span class="string">"q"</span>):</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">cap.release()</span><br><span class="line">cv2.destroyAllWindows()</span><br></pre></td></tr></table></figure><blockquote><p>📝 註：由於本身沒有攝影鏡頭，所以無法示範效果。</p></blockquote><h2 id="💻-匯出為-ONNX"><a href="#💻-匯出為-ONNX" class="headerlink" title="💻 匯出為 ONNX"></a>💻 匯出為 ONNX</h2><p>匯出 ONNX 後可用 <code>cv2.dnn</code> 或 <code>onnxruntime</code> 推論，不依賴 ultralytics：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># export_onnx.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 匯出為 ONNX</span></span><br><span class="line">model.export(format=<span class="string">"onnx"</span>, imgsz=<span class="number">640</span>, dynamic=<span class="literal">False</span>, simplify=<span class="literal">True</span>)</span><br><span class="line"><span class="comment"># 匯出後產生：runs/detect/custom_detector/weights/best.onnx</span></span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-inference/04.png" alt="將訓練好的 YOLOv8 模型匯出為 ONNX 格式，供跨平台部署使用"><br><em>圖：將訓練好的 YOLOv8 模型匯出為 ONNX 格式，供跨平台部署使用</em></p><p><strong>使用 onnxruntime 推論：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference_onnx.py</span></span><br><span class="line"><span class="keyword">import</span> onnxruntime <span class="keyword">as</span> ort</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">session    = ort.InferenceSession(<span class="string">"runs/detect/custom_detector/weights/best.onnx"</span>)</span><br><span class="line">input_name = session.get_inputs()[<span class="number">0</span>].name</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">preprocess</span><span class="params">(img, imgsz=<span class="number">640</span>)</span>:</span></span><br><span class="line">    img_rgb   = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)</span><br><span class="line">    img_rsz   = cv2.resize(img_rgb, (imgsz, imgsz))</span><br><span class="line">    blob      = img_rsz.astype(np.float32) / <span class="number">255.0</span></span><br><span class="line">    <span class="keyword">return</span> np.expand_dims(blob.transpose(<span class="number">2</span>, <span class="number">0</span>, <span class="number">1</span>), axis=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">img  = cv2.imread(<span class="string">"assets/test_cat.jpg"</span>)</span><br><span class="line">blob = preprocess(img)</span><br><span class="line"></span><br><span class="line">outputs = session.run(<span class="literal">None</span>, &#123;input_name: blob&#125;)</span><br><span class="line">print(<span class="string">f"輸出形狀：<span class="subst">&#123;outputs[<span class="number">0</span>].shape&#125;</span>"</span>)</span><br><span class="line"><span class="comment"># 輸出形狀為 (1, 6, 8400)</span></span><br><span class="line"><span class="comment"># 意義說明：</span></span><br><span class="line"><span class="comment">#   - 第一維 1     → batch size</span></span><br><span class="line"><span class="comment">#   - 第二維 6     → 每個候選框包含的資訊數量</span></span><br><span class="line"><span class="comment">#                    = 4（邊界框：x_center, y_center, width, height） </span></span><br><span class="line"><span class="comment">#                    + 1（objectness / confidence） </span></span><br><span class="line"><span class="comment">#                    + 1（你的自訂類別數，目前應該是 2 類：cat 和 dog？）</span></span><br><span class="line"><span class="comment">#   - 第三維 8400  → 候選框總數量（YOLOv8 在 640x640 輸入時通常為 8400）</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># 注意：</span></span><br><span class="line"><span class="comment">#   YOLOv8 的 ONNX 輸出格式與 YOLOv5 不同，已經把 class confidence 與 objectness 合併處理。</span></span><br><span class="line"><span class="comment">#   你必須自行實作：</span></span><br><span class="line"><span class="comment">#   1. 過濾低信心度</span></span><br><span class="line"><span class="comment">#   2. 還原座標 (從 center+wh → xyxy)</span></span><br><span class="line"><span class="comment">#   3. Non-Maximum Suppression (NMS)</span></span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-inference/05.png" alt="使用 onnxruntime 載入 ONNX 模型，對圖片進行前處理並執行推論取得原始輸出張量"><br><em>圖：使用 onnxruntime 載入 ONNX 模型，對圖片進行前處理並執行推論取得原始輸出張量</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><blockquote><p>💡 ONNX 輸出的後處理（NMS、座標還原）較複雜，實際部署建議直接使用 ultralytics 的 Python API，或使用 <code>cv2.dnn.readNetFromONNX</code> 搭配 YOLOv8 的後處理邏輯。</p></blockquote><h2 id="💻-其他匯出格式"><a href="#💻-其他匯出格式" class="headerlink" title="💻 其他匯出格式"></a>💻 其他匯出格式</h2><p>各格式有平台限制，請依環境選擇：</p><table><thead><tr><th>格式</th><th>需求</th><th>適用場景</th></tr></thead><tbody><tr><td><code>engine</code>（TensorRT）</td><td>NVIDIA GPU + TensorRT 安裝</td><td>伺服器端高速推論</td></tr><tr><td><code>coreml</code></td><td>macOS 環境</td><td>iPhone / iPad 部署</td></tr><tr><td><code>tflite</code></td><td>無特殊需求</td><td>Android / 嵌入式裝置</td></tr></tbody></table><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong><code>conf</code> 閾值的設定</strong>：預設為 0.25，太低會有很多誤報，太高會漏掉低信心的正確偵測，需根據應用場景調整。</li><li><strong><code>iou</code> 閾值（NMS）</strong>：預設為 0.7，控制重疊邊界框的過濾，物件密集的場景建議調低（如 0.5）。</li><li><strong><code>verbose=False</code></strong>：在即時推論的迴圈中必須設定，否則每幀都會輸出推論資訊，嚴重影響效能。</li><li><strong>ONNX 的後處理</strong>：YOLOv8 的 ONNX 輸出格式在不同版本間可能略有差異，建議以 ultralytics 的 Python API 為主，ONNX 用於需要脫離 Python 環境的部署場景。</li></ul><h2 id="📊-應用場景"><a href="#📊-應用場景" class="headerlink" title="📊 應用場景"></a>📊 應用場景</h2><ul><li><strong>工廠自動品管</strong>：用訓練好的瑕疵偵測模型，對生產線即時攝影機畫面進行偵測。</li><li><strong>倉庫商品辨識</strong>：偵測並定位貨架上的商品，輔助自動化盤點。</li><li><strong>安全監控</strong>：偵測特定人員或行為，即時發出警報。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>至此，完整的 <strong>YOLOv8 自訓練物件偵測流程</strong>全部走完：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><ul><li>環境安裝 → 資料集準備 → 模型訓練 → 結果分析 → 推論與匯出</li></ul><p>掌握這個流程，就能針對任何你想偵測的物件，從零開始訓練出專屬的 YOLOv8 模型。</p><p>下一篇是 <a href="/python-opencv-20260411-python-opencv-yolov8-faq"><strong>YOLOv8 常見問題 Q&amp;A</strong></a>，整理學完整個流程後最常遇到的問題，包含增加新類別、遷移學習策略與獨立模型的優缺點。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌</p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/predict/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Predict</a><br><a href="https://docs.ultralytics.com/modes/export/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Export</a><br><a href="https://docs.ultralytics.com/usage/python/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Python Usage</a><br><a href="https://onnxruntime.ai/docs/" target="_blank" rel="external nofollow noopener noreferrer">ONNX Runtime 官方文件</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260410-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 訓練結果分析</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260410-python-opencv-yolov8-results/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260410-python-opencv-yolov8-results/</id>
    <published>2026-04-10T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260409-python-opencv-yolov8-training-advanced"><strong>YOLOv8 訓練進階設定</strong></a> 中，我們完成了模型訓練。<br>這一篇介紹如何 <strong>解讀訓練結果</strong>，判斷模型是否訓練良好，以及找出需要改進的方向。</p><p>YOLOv8 在訓練結束後會自動產生大量的圖表和檔案。學會看懂這些資訊，你才能知道：</p><ul><li>模型目前表現如何？</li><li>是否有過擬合？</li><li>哪個類別學得不好？</li><li>還需要繼續訓練還是需要補資料？</li></ul><h2 id="🗃️-訓練結果目錄結構"><a href="#🗃️-訓練結果目錄結構" class="headerlink" title="🗃️ 訓練結果目錄結構"></a>🗃️ 訓練結果目錄結構</h2><p>前一篇 <code>train_full.py</code> 跑完後，結果儲存在 <code>runs/detect/custom_detector/</code>（對應 <code>name=&quot;custom_detector&quot;</code>）。若目錄不存在，請先確認訓練是否正常跑完。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">runs&#x2F;detect&#x2F;custom_detector&#x2F;</span><br><span class="line">├── weights&#x2F;</span><br><span class="line">│   ├── best.pt                      ← 驗證 mAP 最高的模型（用於部署）</span><br><span class="line">│   ├── last.pt                      ← 最後一個 epoch 的模型（用於繼續訓練）</span><br><span class="line">│   └── epoch*.pt                    ← 中間訓練過程的權重（可刪除以節省空間）</span><br><span class="line">├── results.csv                      ← 每個 epoch 的詳細數值（loss、mAP 等）</span><br><span class="line">├── results.png                      ← 訓練曲線圖（Loss + Metrics）</span><br><span class="line">├── confusion_matrix.png             ← 混淆矩陣</span><br><span class="line">├── confusion_matrix_normalized.png</span><br><span class="line">├── BoxPR_curve.png                  ← Box Precision-Recall 曲線</span><br><span class="line">├── BoxF1_curve.png                  ← Box F1-Confidence 曲線</span><br><span class="line">├── BoxP_curve.png                   ← Box Precision-Confidence 曲線</span><br><span class="line">├── BoxR_curve.png                   ← Box Recall-Confidence 曲線</span><br><span class="line">├── val_batch0_labels.jpg            ← 驗證集真實標籤視覺化</span><br><span class="line">├── val_batch0_pred.jpg              ← 驗證集模型預測視覺化</span><br><span class="line">├── labels.jpg                       ← 其他標籤圖（通常是訓練或額外視覺化）</span><br><span class="line">├── train_batch0.jpg                 ← 訓練批次圖（可選保留）</span><br><span class="line">├── train_batch1.jpg</span><br><span class="line">├── train_batch2.jpg</span><br><span class="line">└── args.yaml                        ← 本次訓練使用的所有超參數</span><br></pre></td></tr></table></figure><p><strong>最需要關注的三個檔案：</strong></p><ul><li><code>best.pt</code> → 拿來部署的模型</li><li><code>results.png</code> → 一眼看懂訓練趨勢</li><li><code>confusion_matrix.png</code> → 看哪個類別容易搞錯</li></ul><blockquote><p>💡 <strong><code>best.pt</code> 是部署時使用的模型</strong>，不要用 <code>last.pt</code> 做推論，因為最後一個 epoch 不一定是最好的。</p></blockquote><h2 id="🔎-Loss-曲線怎麼看？（results-png）"><a href="#🔎-Loss-曲線怎麼看？（results-png）" class="headerlink" title="🔎 Loss 曲線怎麼看？（results.png）"></a>🔎 Loss 曲線怎麼看？（results.png）</h2><p><code>results.png</code> 中包含三種 Loss 的訓練與驗證曲線：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><table><thead><tr><th>Loss 名稱</th><th>說明</th></tr></thead><tbody><tr><td><code>box_loss</code></td><td>邊界框座標的回歸損失，越低代表邊界框越準</td></tr><tr><td><code>cls_loss</code></td><td>類別分類損失，越低代表類別辨識越準</td></tr><tr><td><code>dfl_loss</code></td><td>Distribution Focal Loss，邊界框分布損失（YOLOv8 特有）</td></tr></tbody></table><p><strong>正常的訓練曲線應該是：</strong></p><ul><li><code>train/box_loss</code>、<code>train/cls_loss</code>、<code>train/dfl_loss</code> 持續穩定下降</li><li><code>val/box_loss</code>、<code>val/cls_loss</code>、<code>val/dfl_loss</code> 同步下降，並趨於平穩</li></ul><p><strong>異常情況判斷：</strong></p><table><thead><tr><th>現象</th><th>可能原因</th><th>解法</th></tr></thead><tbody><tr><td>train loss 下降但 val loss 上升</td><td>過擬合</td><td>增加資料、提高 <code>weight_decay</code>、降低 <code>epochs</code></td></tr><tr><td>train/val loss 都不下降</td><td>學習率太小或資料有問題</td><td>提高 <code>lr0</code>，重新確認標籤格式</td></tr><tr><td>Loss 震盪劇烈</td><td>學習率太大或 <code>batch</code> 太小</td><td>降低 <code>lr0</code>，增加 <code>batch</code></td></tr><tr><td>Loss 為 NaN</td><td>標籤座標超出 0~1 範圍</td><td>重新確認標籤格式</td></tr></tbody></table><p><img loading="lazy" src="/images/python/opencv/yolov8-results/01.png" alt="results.png 訓練曲線圖，包含各項 Loss 與 mAP 指標的每個 epoch 變化"><br><em>圖：results.png 訓練曲線圖，包含各項 Loss 與 mAP 指標的每個 epoch 變化</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><h2 id="🔎-mAP-指標怎麼看？"><a href="#🔎-mAP-指標怎麼看？" class="headerlink" title="🔎 mAP 指標怎麼看？"></a>🔎 mAP 指標怎麼看？</h2><p>mAP（mean Average Precision）是物件偵測最重要的評估指標，有三個地方可以看到：</p><ol><li><strong>訓練時的終端機輸出</strong>：每個 epoch 結束後會印出當前的 mAP50 與 mAP50-95</li><li><strong><code>results.png</code></strong>：訓練結束後自動產生，包含完整的 mAP 曲線圖，在 <code>runs/detect/custom_detector/</code> 下</li><li><strong><code>results.csv</code></strong>：每個 epoch 的詳細數值，可用程式讀取（見本篇下方的範例）</li></ol><p>各指標的意義：</p><table><thead><tr><th>指標</th><th>說明</th></tr></thead><tbody><tr><td><code>mAP50</code></td><td>IoU 閾值 = 0.5 時的 mAP，只要邊界框重疊超過 50% 就算正確，是最常用的指標</td></tr><tr><td><code>mAP50-95</code></td><td>IoU 閾值從 0.5 到 0.95 的平均 mAP，要求更嚴格的邊界框定位精度，通常比 mAP50 低很多</td></tr><tr><td><code>Precision</code></td><td>模型預測出的框中，有多少比例是真的有物件（誤報率低）</td></tr><tr><td><code>Recall</code></td><td>實際存在的物件中，有多少比例被模型找到了（漏報率低）</td></tr></tbody></table><p><strong>mAP50 一般參考標準：</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><table><thead><tr><th>mAP50 範圍</th><th>評估等級</th></tr></thead><tbody><tr><td>&lt; 0.40</td><td>較差，需要大幅改善資料或標註</td></tr><tr><td>0.40 ~ 0.60</td><td>普通，可以接受但還有進步空間</td></tr><tr><td>0.60 ~ 0.75</td><td>良好</td></tr><tr><td>&gt; 0.75</td><td>優秀</td></tr></tbody></table><blockquote><p>💡 自訂資料集的 mAP 沒有絕對標準，最重要的是「是否滿足應用需求」。例如安全監控對 Recall 要求高（不能漏報），商品辨識則對 Precision 要求高（不能誤報）。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/yolov8-results/02.png" alt="終端機輸出、results.png、results.csv，包含各項 Loss 與 mAP 指標的每個 epoch 變化"><br><em>圖：終端機輸出、results.png、results.csv，包含各項 Loss 與 mAP 指標的每個 epoch 變化</em></p><h2 id="🔎-混淆矩陣怎麼看？"><a href="#🔎-混淆矩陣怎麼看？" class="headerlink" title="🔎 混淆矩陣怎麼看？"></a>🔎 混淆矩陣怎麼看？</h2><p>YOLOv8 會產生兩張混淆矩陣圖：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><ul><li><strong><code>confusion_matrix.png</code></strong>：顯示實際的預測數量</li><li><strong><code>confusion_matrix_normalized.png</code></strong>：<strong>推薦查看</strong>（每個類別的預測比例，0~1）</li></ul><p><strong>重點看 <code>confusion_matrix_normalized.png</code></strong>：</p><ul><li><strong>對角線顏色越深（接近 1.0）</strong>：該類別辨識越準確</li><li><strong>非對角線有明顯顏色</strong>：模型常把 A 類別誤認為 B 類別（這就是最需要改進的地方）</li><li><strong>background 那一行/列</strong>：<ul><li>右邊的 background FP：模型誤檢（把背景當成物件）</li><li>下方的 background FN：模型漏檢（沒找到實際存在的物件）</li></ul></li></ul><p>看這張圖可以快速找出「哪個類別最容易搞錯」，然後針對性地補充該類別的資料或改善標註品質。</p><p><img loading="lazy" src="/images/python/opencv/yolov8-results/03.png" alt="confusion_matrix_normalized.png 範例，對角線顏色越接近 1.0 代表該類別分類越準確，非對角線的顏色代表容易混淆的類別"><br><em>圖：confusion_matrix_normalized.png 範例，對角線顏色越接近 1.0 代表該類別分類越準確，非對角線的顏色代表容易混淆的類別</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><h2 id="💻-用程式讀取訓練結果"><a href="#💻-用程式讀取訓練結果" class="headerlink" title="💻 用程式讀取訓練結果"></a>💻 用程式讀取訓練結果</h2><p>本節需要額外安裝 <code>pandas</code> 與 <code>matplotlib</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install pandas matplotlib</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># analyze_results.py</span></span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line">df = pd.read_csv(<span class="string">"runs/detect/custom_detector/results.csv"</span>)</span><br><span class="line">df.columns = df.columns.str.strip()   <span class="comment"># 去除欄位名稱的空白</span></span><br><span class="line"></span><br><span class="line">print(df.columns.tolist())            <span class="comment"># 查看所有欄位名稱</span></span><br><span class="line">print(df.tail(<span class="number">5</span>))                     <span class="comment"># 查看最後 5 個 epoch 的數值</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 找出最佳 epoch</span></span><br><span class="line">best_epoch = df[<span class="string">"metrics/mAP50(B)"</span>].idxmax()</span><br><span class="line">print(<span class="string">f"\n最佳 epoch：<span class="subst">&#123;best_epoch + <span class="number">1</span>&#125;</span>"</span>)</span><br><span class="line">print(<span class="string">f"最佳 mAP50：<span class="subst">&#123;df[<span class="string">'metrics/mAP50(B)'</span>].max():<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line">print(<span class="string">f"最佳 mAP50-95：<span class="subst">&#123;df[<span class="string">'metrics/mAP50-95(B)'</span>].max():<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 繪製 mAP 曲線</span></span><br><span class="line">plt.figure(figsize=(<span class="number">10</span>, <span class="number">4</span>))</span><br><span class="line">plt.plot(df[<span class="string">"metrics/mAP50(B)"</span>],    label=<span class="string">"mAP50"</span>)</span><br><span class="line">plt.plot(df[<span class="string">"metrics/mAP50-95(B)"</span>], label=<span class="string">"mAP50-95"</span>)</span><br><span class="line">plt.xlabel(<span class="string">"Epoch"</span>)</span><br><span class="line">plt.ylabel(<span class="string">"mAP"</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.title(<span class="string">"mAP Training Curve"</span>)</span><br><span class="line">plt.savefig(<span class="string">"mAP_curve.png"</span>)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-results/04.png" alt="讀取 results.csv 找出最佳 epoch 並繪製 mAP50 與 mAP50-95 的訓練曲線圖"><br><em>圖：讀取 results.csv 找出最佳 epoch 並繪製 mAP50 與 mAP50-95 的訓練曲線圖</em></p><h2 id="💻-執行驗證取得詳細報告"><a href="#💻-執行驗證取得詳細報告" class="headerlink" title="💻 執行驗證取得詳細報告"></a>💻 執行驗證取得詳細報告</h2><p>訓練完成後，可單獨對驗證集執行評估取得更詳細的結果：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># evaluate.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"><span class="keyword">import</span> multiprocessing   <span class="comment"># 加上這行</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">    <span class="comment"># 解決 Windows multiprocessing 錯誤</span></span><br><span class="line">    multiprocessing.freeze_support()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 載入模型</span></span><br><span class="line">    model = YOLO(<span class="string">"runs/detect/custom_detector/weights/best.pt"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 執行驗證（強制關閉多進程，避免 Windows 問題）</span></span><br><span class="line">    metrics = model.val(</span><br><span class="line">        data=<span class="string">"data.yaml"</span>,</span><br><span class="line">        device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,</span><br><span class="line">        workers=<span class="number">0</span>,           <span class="comment"># ← 關鍵！設成 0 比較穩定</span></span><br><span class="line">        batch=<span class="number">16</span>,            <span class="comment"># 可根據你的顯卡調整（太大也會出問題）</span></span><br><span class="line">        imgsz=<span class="number">640</span>,           <span class="comment"># 如果你訓練時用其他尺寸，可以指定</span></span><br><span class="line">        plots=<span class="literal">True</span>           <span class="comment"># 想產生 confusion_matrix 等圖片就開啟</span></span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 印出指標</span></span><br><span class="line">    print(<span class="string">f"mAP50：    <span class="subst">&#123;metrics.box.map50:<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line">    print(<span class="string">f"mAP50-95： <span class="subst">&#123;metrics.box.map:<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line">    print(<span class="string">f"Precision：<span class="subst">&#123;metrics.box.mp:<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line">    print(<span class="string">f"Recall：   <span class="subst">&#123;metrics.box.mr:<span class="number">.4</span>f&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    print(<span class="string">"\n各類別 AP50："</span>)</span><br><span class="line">    <span class="keyword">for</span> i, (name, ap) <span class="keyword">in</span> enumerate(zip(metrics.names.values(), metrics.box.ap50)):</span><br><span class="line">        print(<span class="string">f"  <span class="subst">&#123;name&#125;</span>: <span class="subst">&#123;ap:<span class="number">.4</span>f&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-results/05.png" alt="載入最佳模型對驗證集執行評估，輸出 mAP50、mAP50-95、Precision、Recall 及各類別 AP50"><br><em>圖：載入最佳模型對驗證集執行評估，輸出 mAP50、mAP50-95、Precision、Recall 及各類別 AP50</em></p><h2 id="🔎-val-batch-視覺化"><a href="#🔎-val-batch-視覺化" class="headerlink" title="🔎 val_batch 視覺化"></a>🔎 val_batch 視覺化</h2><p><code>val_batch0_labels.jpg</code> 與 <code>val_batch0_pred.jpg</code> 是驗證集第一批次的對照圖：</p><ul><li><strong>Labels</strong>：實際標註的邊界框</li><li><strong>Pred</strong>：模型預測的邊界框</li></ul><p>直接比較這兩張圖，能快速判斷模型的偵測品質與定位精度。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><blockquote><p>💡 <strong>本範例說明</strong>：這裡使用的標籤是以預標籤（Pre-labeling）方式自動生成，並未使用 LabelImg 逐一手動修正，因此 Labels 圖中可以看到部分標記有誤（例如邊界框位置不準或類別標錯）。這也說明了標註品質對訓練結果的影響——如果 Labels 本身就有錯，模型學到的也會是錯誤的邊界框位置，進而拉低 mAP。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/yolov8-results/07.png" alt="val_batch0_labels.jpg — 驗證集實際標註的邊界框（因使用預標籤且未手動修正，部分標記有誤）"><br><em>圖：val_batch0_labels.jpg — 驗證集實際標註的邊界框（因使用預標籤且未手動修正，部分標記有誤）</em></p><p><img loading="lazy" src="/images/python/opencv/yolov8-results/08.png" alt="val_batch0_pred.jpg — 模型預測的邊界框，與上圖對照判斷偵測品質"><br><em>圖：val_batch0_pred.jpg — 模型預測的邊界框，與上圖對照判斷偵測品質</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>mAP 低不一定代表模型差</strong>：如果資料量少、類別困難、或標註品質差，mAP 自然偏低，先確認資料品質。</li><li><strong><code>best.pt</code> 與 <code>last.pt</code> 的差異</strong>：<code>best.pt</code> 是 mAP 最高的 epoch，不一定是最後一個 epoch。建議以 <code>best.pt</code> 部署。</li><li><strong>Early Stopping 後的 <code>last.pt</code></strong>：若訓練因 Early Stopping 提早結束，<code>last.pt</code> 是停止時的狀態，不一定是最好的，還是要用 <code>best.pt</code>。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>學會解讀訓練結果，才能有效判斷模型是否需要更多資料、更長訓練時間，或調整超參數。<br>下一步是 <a href="/python-opencv-20260411-python-opencv-yolov8-inference"><strong>YOLOv8 推論與匯出</strong></a>，把訓練好的模型實際應用在圖片、影片或即時攝影機上。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/val/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Val</a><br><a href="https://docs.ultralytics.com/modes/train/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Train</a><br><a href="https://jonathan-hui.medium.com/map-mean-average-precision-for-object-detection-45c121a31173" target="_blank" rel="external nofollow noopener noreferrer">Mean Average Precision — 物件偵測評估指標說明</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​‌​​‌‌​​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​‌​​‌‌​​‌​‌​‌‌‌​​‌‌​‌‌‌​‌​‌​‌‌​‌‌​​​‌‌‌​‌​​​‌‌‌​​‌‌</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260409-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 訓練進階設定</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260409-python-opencv-yolov8-training-advanced/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260409-python-opencv-yolov8-training-advanced/</id>
    <published>2026-04-09T10:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260409-python-opencv-yolov8-training"><strong>YOLOv8 模型訓練</strong></a> 中，我們完成了基本的訓練流程設定。</p><p>但「能訓練」和「訓練得好」是兩回事。</p><p>這一篇將介紹兩個非常重要的進階主題：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌​​​‌‌‌​‌‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​​‌​​</p><ul><li><strong>資料增強</strong>：讓模型看到更多變化，幫助它學得更好</li><li><strong>防止過擬合</strong>：避免模型死背訓練資料，真正具備泛化能力</li></ul><p>掌握這些設定後，你的模型訓練效果會明顯提升。</p><h2 id="💻-內建資料增強設定"><a href="#💻-內建資料增強設定" class="headerlink" title="💻 內建資料增強設定"></a>💻 內建資料增強設定</h2><p>YOLOv8 在訓練時內建了豐富的資料增強，<strong>預設已自動啟用</strong>，不需要額外撰寫增強程式碼。<br>一般情況下預設值已夠用，不需要動。但以下幾種情況可能需要調整：</p><ul><li><strong>資料集很小（&lt; 100 張）</strong>：可以加強增強強度，讓模型看到更多變化，減少過擬合</li><li><strong>物件方向固定（如俯視圖）</strong>：可以關閉 <code>fliplr</code>、<code>flipud</code>，避免產生現實中不存在的翻轉角度</li><li><strong>室內固定光源場景</strong>：<code>hsv_s</code>、<code>hsv_v</code> 可以調低，不需要大幅模擬色彩變化</li><li><strong>訓練結果不穩定或 mAP 很低</strong>：可以嘗試設 <code>mosaic=0.0</code>，有時過強的增強反而干擾學習</li></ul><p>可透過參數調整增強強度：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌​​​‌‌‌​‌‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​​‌​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># augmentation_params.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line"></span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data.yaml"</span>,</span><br><span class="line">    epochs=<span class="number">100</span>,</span><br><span class="line">    device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,</span><br><span class="line">    <span class="comment"># 幾何增強</span></span><br><span class="line">    fliplr=<span class="number">0.5</span>,       <span class="comment"># 水平翻轉機率（預設 0.5）</span></span><br><span class="line">    flipud=<span class="number">0.0</span>,       <span class="comment"># 垂直翻轉機率（預設 0.0，通常不需要）</span></span><br><span class="line">    degrees=<span class="number">0.0</span>,      <span class="comment"># 旋轉角度範圍 ±degrees</span></span><br><span class="line">    translate=<span class="number">0.1</span>,    <span class="comment"># 平移比例</span></span><br><span class="line">    scale=<span class="number">0.5</span>,        <span class="comment"># 縮放範圍（0.5 表示 0.5x ~ 1.5x）</span></span><br><span class="line">    shear=<span class="number">0.0</span>,        <span class="comment"># 剪切角度</span></span><br><span class="line">    perspective=<span class="number">0.0</span>,  <span class="comment"># 透視變換</span></span><br><span class="line">    <span class="comment"># 色彩增強</span></span><br><span class="line">    hsv_h=<span class="number">0.015</span>,      <span class="comment"># 色相抖動</span></span><br><span class="line">    hsv_s=<span class="number">0.7</span>,        <span class="comment"># 飽和度抖動</span></span><br><span class="line">    hsv_v=<span class="number">0.4</span>,        <span class="comment"># 明度抖動</span></span><br><span class="line">    <span class="comment"># 進階增強</span></span><br><span class="line">    mosaic=<span class="number">1.0</span>,       <span class="comment"># Mosaic 增強（將 4 張圖拼成 1 張，1.0 = 100% 啟用）</span></span><br><span class="line">    mixup=<span class="number">0.0</span>,        <span class="comment"># MixUp 增強機率</span></span><br><span class="line">    copy_paste=<span class="number">0.0</span>,   <span class="comment"># Copy-Paste 增強機率</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><blockquote><p>💡 <strong>Mosaic 增強</strong>是 YOLO 的招牌技術，將 4 張圖片拼接在一起，讓模型學習更多背景多樣性，對偵測小物件特別有效。</p></blockquote><h2 id="💻-防止過擬合設定"><a href="#💻-防止過擬合設定" class="headerlink" title="💻 防止過擬合設定"></a>💻 防止過擬合設定</h2><p>過擬合是指模型在訓練集表現很好，但在驗證集表現變差。</p><p>YOLOv8 提供了以下常見防過擬合機制​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌​​​‌‌‌​‌‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​​‌​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># anti_overfit_example.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line"></span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data.yaml"</span>,</span><br><span class="line">    epochs=<span class="number">150</span>,</span><br><span class="line">    imgsz=<span class="number">640</span>,</span><br><span class="line">    batch=<span class="number">16</span>,</span><br><span class="line">    device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># === 防過擬合核心參數 ===</span></span><br><span class="line">    patience=<span class="number">30</span>,           <span class="comment"># 連續 30 個 epoch 驗證指標沒改善就自動停止（Early Stopping）</span></span><br><span class="line">    weight_decay=<span class="number">0.0005</span>,   <span class="comment"># L2 正規化，防止權重過大</span></span><br><span class="line">    dropout=<span class="number">0.0</span>,           <span class="comment"># Dropout（過擬合嚴重時可試 0.1 ~ 0.3）</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 其他常用設定</span></span><br><span class="line">    lr0=<span class="number">0.01</span>,              <span class="comment"># 初始學習率</span></span><br><span class="line">    lrf=<span class="number">0.01</span>,              <span class="comment"># 最終學習率比例</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><blockquote><p>💡 如何判斷是否過擬合、怎麼讀懂訓練曲線，將在 <a href="/python-opencv-20260410-python-opencv-yolov8-results"><strong>YOLOv8 訓練結果分析</strong></a> 中完整說明。</p></blockquote><h2 id="💻-完整訓練範例"><a href="#💻-完整訓練範例" class="headerlink" title="💻 完整訓練範例"></a>💻 完整訓練範例</h2><p>整合基本參數、資料增強與防過擬合設定：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># train_full.py</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"使用裝置：<span class="subst">&#123;<span class="string">'GPU - '</span> + torch.cuda.get_device_name(<span class="number">0</span>) <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">'CPU'</span>&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8s.pt"</span>)</span><br><span class="line"></span><br><span class="line">start = time.time()</span><br><span class="line"></span><br><span class="line">model.train(</span><br><span class="line">    data=<span class="string">"data.yaml"</span>,</span><br><span class="line">    epochs=<span class="number">120</span>,</span><br><span class="line">    imgsz=<span class="number">640</span>,</span><br><span class="line">    batch=<span class="number">16</span>,</span><br><span class="line">    device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,</span><br><span class="line">    workers=<span class="number">0</span>,                    <span class="comment"># Windows 必須設 0</span></span><br><span class="line">    name=<span class="string">"custom_detector"</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 防過擬合</span></span><br><span class="line">    patience=<span class="number">30</span>,</span><br><span class="line">    weight_decay=<span class="number">0.0005</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 資料增強</span></span><br><span class="line">    mosaic=<span class="number">1.0</span>,</span><br><span class="line">    fliplr=<span class="number">0.5</span>,</span><br><span class="line">    hsv_h=<span class="number">0.015</span>,</span><br><span class="line">    hsv_s=<span class="number">0.7</span>,</span><br><span class="line">    hsv_v=<span class="number">0.4</span>,</span><br><span class="line">    </span><br><span class="line">    <span class="comment"># 其他實用設定</span></span><br><span class="line">    save_period=<span class="number">10</span>,               <span class="comment"># 每 10 個 epoch 存一次</span></span><br><span class="line">    plots=<span class="literal">True</span>,                   <span class="comment"># 產生訓練曲線圖</span></span><br><span class="line">    verbose=<span class="literal">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">elapsed = time.time() - start</span><br><span class="line">print(<span class="string">f"訓練完成，總耗時：<span class="subst">&#123;elapsed // <span class="number">3600</span>:<span class="number">.0</span>f&#125;</span> 時 <span class="subst">&#123;elapsed % <span class="number">3600</span> // <span class="number">60</span>:<span class="number">.0</span>f&#125;</span> 分 <span class="subst">&#123;elapsed % <span class="number">60</span>:<span class="number">.0</span>f&#125;</span> 秒"</span>)</span><br><span class="line">print(<span class="string">"最佳模型：runs/detect/custom_detector/weights/best.pt"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-training-advanced/01.png" alt="完整訓練範例，整合資料增強、Early Stopping、定期儲存等設定並輸出最佳模型路徑"><br><em>圖：完整訓練範例，整合資料增強、Early Stopping、定期儲存等設定並輸出最佳模型路徑</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌​​​‌‌‌​‌‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​​‌​​</p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong><code>patience</code> 不要設太小</strong>：YOLOv8 訓練初期的 mAP 可能波動較大，<code>patience=10</code> 可能會太早停止。建議至少設 20~30。</li><li><strong>增強設定需配合資料集特性</strong>：並非增強越強越好，過強的增強（例如對方向固定的物件開啟翻轉）反而會讓模型學到錯誤的模式。</li><li><strong>先用小 epochs 測試</strong>：正式訓練前，建議先用 <code>epochs=10</code> 跑一次，確認參數設定合理後再調高。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>資料增強與防過擬合是讓模型從「能跑」進化到「訓練得好」的關鍵設定。<br>適當調整這些參數，能讓你的模型在資料量有限的情況下，仍然獲得不錯的泛化能力。<br>下一步是 <a href="/python-opencv-20260410-python-opencv-yolov8-results"><strong>YOLOv8 訓練結果分析</strong></a>，學習如何看懂訓練過程產生的各種圖表與指標，判斷模型是否真的學好了。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/train/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Train</a><br><a href="https://docs.ultralytics.com/usage/cfg/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Configuration</a><br><a href="https://docs.ultralytics.com/reference/data/augment/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Augmentation</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌​​‌​‌‌​‌​‌‌​​​​‌​‌‌​​‌​​​‌‌‌​‌‌​​‌‌​​​​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌​​‌​‌​‌‌​​‌​​</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260409-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 模型訓練</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260409-python-opencv-yolov8-training/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260409-python-opencv-yolov8-training/</id>
    <published>2026-04-09T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.557Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260408-python-opencv-yolov8-pre-label"><strong>YOLOv8 預標籤（Pre-Label）</strong></a> 中，我們用預訓練模型自動產生標籤草稿，再透過 LabelImg 人工修正，完成了標記工作。<br>這一篇進入最核心的環節：<strong>YOLOv8 模型訓練</strong>。</p><p>YOLOv8 的訓練指令非常簡潔，但背後有許多重要參數影響訓練效果。<br>這一篇說明每個關鍵參數的意義，以及 GPU 加速與中斷恢復的設定方式。</p><h2 id="🔎-開始訓練前的確認"><a href="#🔎-開始訓練前的確認" class="headerlink" title="🔎 開始訓練前的確認"></a>🔎 開始訓練前的確認</h2><p>在開始訓練之前，請確認前幾篇的工作都已完成，你手上應該有以下結構：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">project&#x2F;</span><br><span class="line">├── raw_images&#x2F;          ← 原始圖片（20260407 收集）</span><br><span class="line">├── raw_labels&#x2F;          ← 預標籤 + 人工修正後的 .txt（20260408 完成）</span><br><span class="line">├── dataset&#x2F;</span><br><span class="line">│   ├── images&#x2F;</span><br><span class="line">│   │   ├── train&#x2F;       ← 訓練集圖片</span><br><span class="line">│   │   └── val&#x2F;         ← 驗證集圖片</span><br><span class="line">│   └── labels&#x2F;</span><br><span class="line">│       ├── train&#x2F;       ← 訓練集標籤</span><br><span class="line">│       └── val&#x2F;         ← 驗證集標籤</span><br><span class="line">├── data.yaml            ← 訓練用設定檔（20260407 建立）</span><br><span class="line">└── train.py             ← 本篇將撰寫此檔案</span><br></pre></td></tr></table></figure><h2 id="💻-基本訓練指令"><a href="#💻-基本訓練指令" class="headerlink" title="💻 基本訓練指令"></a>💻 基本訓練指令</h2><h3 id="🔹-指令列方式"><a href="#🔹-指令列方式" class="headerlink" title="🔹 指令列方式"></a>🔹 指令列方式</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">yolo detect train \</span><br><span class="line">  model=yolov8n.pt \</span><br><span class="line">  data=data.yaml \</span><br><span class="line">  epochs=100 \</span><br><span class="line">  imgsz=640 \</span><br><span class="line">  batch=16 \</span><br><span class="line">  device=0         <span class="comment"># 無 GPU 請改為 device=cpu</span></span><br></pre></td></tr></table></figure><h3 id="🔹-Python-API-方式"><a href="#🔹-Python-API-方式" class="headerlink" title="🔹 Python API 方式"></a>🔹 Python API 方式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># train.py</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)  <span class="comment"># 以預訓練模型為起點（第一次執行會自動下載）</span></span><br><span class="line"></span><br><span class="line">start = time.time()</span><br><span class="line"></span><br><span class="line">results = model.train(</span><br><span class="line">    data=<span class="string">"data.yaml"</span>,                                  <span class="comment"># 資料集設定檔路徑（由 20260407 的 create_yaml.py 產生）</span></span><br><span class="line">    epochs=<span class="number">100</span>,                                        <span class="comment"># 訓練總輪數，初次建議先設 3~5 確認流程</span></span><br><span class="line">    imgsz=<span class="number">640</span>,                                         <span class="comment"># 輸入圖片尺寸，越大越準確但越慢、越吃記憶體</span></span><br><span class="line">    batch=<span class="number">16</span>,                                          <span class="comment"># 每批次圖片數，GPU 記憶體不足時調小（8、4）</span></span><br><span class="line">    device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>,  <span class="comment"># 自動偵測 GPU，無 GPU 退回 CPU</span></span><br><span class="line">    workers=<span class="number">0</span>,                                         <span class="comment"># 資料載入執行緒數，Windows 必須設 0</span></span><br><span class="line">    name=<span class="string">"my_model"</span>,                                   <span class="comment"># 訓練結果子目錄，結果存在 runs/detect/my_model/</span></span><br><span class="line">    save_period=<span class="number">10</span>,                                    <span class="comment"># 每 10 個 epoch 儲存一次 checkpoint</span></span><br><span class="line">    plots=<span class="literal">True</span>,                                        <span class="comment"># 自動產生訓練曲線圖表</span></span><br><span class="line">    verbose=<span class="literal">True</span>,                                      <span class="comment"># 顯示每個 epoch 的詳細訓練資訊</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">elapsed = time.time() - start</span><br><span class="line">print(<span class="string">f"訓練完成，總耗時：<span class="subst">&#123;elapsed // <span class="number">3600</span>:<span class="number">.0</span>f&#125;</span> 時 <span class="subst">&#123;elapsed % <span class="number">3600</span> // <span class="number">60</span>:<span class="number">.0</span>f&#125;</span> 分 <span class="subst">&#123;elapsed % <span class="number">60</span>:<span class="number">.0</span>f&#125;</span> 秒"</span>)</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line">D:\PythonWorkspace\yolov8-tutorial\.venv\Scripts\python.exe D:\PythonWorkspace\yolov8-tutorial\train.py </span><br><span class="line">Ultralytics 8.4.37  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">engine\trainer: agnostic_nms=False, amp=True, angle=1.0, augment=False, auto_augment=randaugment, batch=16, </span><br><span class="line">bgr=0.0, box=7.5, cache=False, cfg=None, classes=None, close_mosaic=10, cls=0.5, cls_pw=0.0, compile=False, </span><br><span class="line">conf=None, copy_paste=0.0, copy_paste_mode=flip, cos_lr=False, cutmix=0.0, data=data.yaml, degrees=0.0, </span><br><span class="line">deterministic=True, device=0, dfl=1.5, dnn=False, dropout=0.0, dynamic=False, embed=None, end2end=None, </span><br><span class="line">epochs=100, erasing=0.4, exist_ok=False, fliplr=0.5, flipud=0.0, format=torchscript, fraction=1.0, </span><br><span class="line">freeze=None, half=False, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, imgsz=640, int8=False, iou=0.7, keras=False, </span><br><span class="line">kobj=1.0, line_width=None, lr0=0.01, lrf=0.01, mask_ratio=4, max_det=300, mixup=0.0, mode=train, </span><br><span class="line">model=yolov8n.pt, momentum=0.937, mosaic=1.0, multi_scale=0.0, name=my_model, nbs=64, nms=False, </span><br><span class="line">opset=None, optimize=False, optimizer=auto, overlap_mask=True, patience=100, perspective=0.0, plots=True, </span><br><span class="line">pose=12.0, pretrained=True, profile=False, project=None, rect=False, resume=False, retina_masks=False, </span><br><span class="line">rle=1.0, save=True, save_conf=False, save_crop=False, save_dir=D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model, </span><br><span class="line">save_frames=False, save_json=False, save_period=10, save_txt=False, scale=0.5, seed=0, shear=0.0, show=False, </span><br><span class="line">show_boxes=True, show_conf=True, show_labels=True, simplify=True, single_cls=False, <span class="built_in">source</span>=None, split=val, </span><br><span class="line">stream_buffer=False, task=detect, time=None, tracker=botsort.yaml, translate=0.1, val=True, verbose=True, </span><br><span class="line">vid_stride=1, visualize=False, warmup_bias_lr=0.1, warmup_epochs=3.0, warmup_momentum=0.8, weight_decay=0.0005, </span><br><span class="line">workers=0, workspace=None</span><br><span class="line">Overriding model.yaml nc=80 with nc=2</span><br><span class="line"></span><br><span class="line">                   from  n    params  module                                       arguments                     </span><br><span class="line">  0                  -1  1       464  ultralytics.nn.modules.conv.Conv             [3, 16, 3, 2]                 </span><br><span class="line">  1                  -1  1      4672  ultralytics.nn.modules.conv.Conv             [16, 32, 3, 2]                </span><br><span class="line">  2                  -1  1      7360  ultralytics.nn.modules.block.C2f             [32, 32, 1, True]             </span><br><span class="line">  3                  -1  1     18560  ultralytics.nn.modules.conv.Conv             [32, 64, 3, 2]                </span><br><span class="line">  4                  -1  2     49664  ultralytics.nn.modules.block.C2f             [64, 64, 2, True]             </span><br><span class="line">  5                  -1  1     73984  ultralytics.nn.modules.conv.Conv             [64, 128, 3, 2]               </span><br><span class="line">  6                  -1  2    197632  ultralytics.nn.modules.block.C2f             [128, 128, 2, True]           </span><br><span class="line">  7                  -1  1    295424  ultralytics.nn.modules.conv.Conv             [128, 256, 3, 2]              </span><br><span class="line">  8                  -1  1    460288  ultralytics.nn.modules.block.C2f             [256, 256, 1, True]           </span><br><span class="line">  9                  -1  1    164608  ultralytics.nn.modules.block.SPPF            [256, 256, 5]                 </span><br><span class="line"> 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, <span class="string">'nearest'</span>]          </span><br><span class="line"> 11             [-1, 6]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           </span><br><span class="line"> 12                  -1  1    148224  ultralytics.nn.modules.block.C2f             [384, 128, 1]                 </span><br><span class="line"> 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, <span class="string">'nearest'</span>]          </span><br><span class="line"> 14             [-1, 4]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           </span><br><span class="line"> 15                  -1  1     37248  ultralytics.nn.modules.block.C2f             [192, 64, 1]                  </span><br><span class="line"> 16                  -1  1     36992  ultralytics.nn.modules.conv.Conv             [64, 64, 3, 2]                </span><br><span class="line"> 17            [-1, 12]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           </span><br><span class="line"> 18                  -1  1    123648  ultralytics.nn.modules.block.C2f             [192, 128, 1]                 </span><br><span class="line"> 19                  -1  1    147712  ultralytics.nn.modules.conv.Conv             [128, 128, 3, 2]              </span><br><span class="line"> 20             [-1, 9]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           </span><br><span class="line"> 21                  -1  1    493056  ultralytics.nn.modules.block.C2f             [384, 256, 1]                 </span><br><span class="line"> 22        [15, 18, 21]  1    751702  ultralytics.nn.modules.head.Detect           [2, 16, None, [64, 128, 256]] </span><br><span class="line">Model summary: 130 layers, 3,011,238 parameters, 3,011,222 gradients, 8.2 GFLOPs</span><br><span class="line"></span><br><span class="line">Transferred 319/355 items from pretrained weights</span><br><span class="line">Freezing layer <span class="string">'model.22.dfl.conv.weight'</span></span><br><span class="line">AMP: running Automatic Mixed Precision (AMP) checks...</span><br><span class="line">AMP: checks passed </span><br><span class="line">train: Fast image access  (ping: 0.00.0 ms, <span class="built_in">read</span>: 1838.31089.4 MB/s, size: 174.8 KB)</span><br><span class="line">train: Scanning D:\PythonWorkspace\yolov8-tutorial\dataset\labels\train.cache... 98 images, 15 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 98/98  0.0s</span><br><span class="line">val: Fast image access  (ping: 0.00.0 ms, <span class="built_in">read</span>: 2107.01291.3 MB/s, size: 131.0 KB)</span><br><span class="line">val: Scanning D:\PythonWorkspace\yolov8-tutorial\dataset\labels\val.cache... 24 images, 1 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 24/24  0.0s</span><br><span class="line">optimizer: <span class="string">'optimizer=auto'</span> found, ignoring <span class="string">'lr0=0.01'</span> and <span class="string">'momentum=0.937'</span> and determining best <span class="string">'optimizer'</span>, <span class="string">'lr0'</span> and <span class="string">'momentum'</span> automatically... </span><br><span class="line">optimizer: AdamW(lr=0.001667, momentum=0.9) with parameter groups 57 weight(decay=0.0), 64 weight(decay=0.0005), 63 bias(decay=0.0)</span><br><span class="line">Plotting labels to D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model\labels.jpg... </span><br><span class="line">Image sizes 640 train, 640 val</span><br><span class="line">Using 0 dataloader workers</span><br><span class="line">Logging results to D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model</span><br><span class="line">Starting training <span class="keyword">for</span> 100 epochs...</span><br><span class="line"></span><br><span class="line">      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size</span><br><span class="line">      1/100      2.02G     0.6719      2.927      1.129          5        640: 100% ━━━━━━━━━━━━ 7/7 2.2it/s 3.3s</span><br><span class="line">                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 1/1 2.2it/s 0.4s</span><br><span class="line">                   all         24         30     0.0041          1      0.557      0.489</span><br><span class="line"></span><br><span class="line">  ... 中間省略 ...</span><br><span class="line"></span><br><span class="line">      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size</span><br><span class="line">     90/100      2.18G      0.448     0.5201     0.9692          8        640: 100% ━━━━━━━━━━━━ 7/7 4.3it/s 1.6s</span><br><span class="line">                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 1/1 3.1it/s 0.3s</span><br><span class="line">                   all         24         30      0.936      0.938      0.979      0.758</span><br><span class="line">Closing dataloader mosaic</span><br><span class="line"></span><br><span class="line">  ... 中間省略 ...</span><br><span class="line">  </span><br><span class="line">      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size</span><br><span class="line">    100/100      2.18G     0.3441     0.5115     0.8918          3        640: 100% ━━━━━━━━━━━━ 7/7 4.5it/s 1.6s</span><br><span class="line">                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 1/1 3.1it/s 0.3s</span><br><span class="line">                   all         24         30      0.851        0.9      0.941       0.69</span><br><span class="line"></span><br><span class="line">100 epochs completed <span class="keyword">in</span> 0.065 hours.</span><br><span class="line">Optimizer stripped from D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model\weights\last.pt, 6.3MB</span><br><span class="line">Optimizer stripped from D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model\weights\best.pt, 6.3MB</span><br><span class="line"></span><br><span class="line">Validating D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model\weights\best.pt...</span><br><span class="line">Ultralytics 8.4.37  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">Model summary (fused): 73 layers, 3,006,038 parameters, 0 gradients, 8.1 GFLOPs</span><br><span class="line">                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 1/1 2.9it/s 0.3s</span><br><span class="line">                   all         24         30       0.95      0.933      0.979       0.76</span><br><span class="line">                   cat         18         25      0.899       0.92      0.964       0.57</span><br><span class="line">                   dog          5          5          1      0.945      0.995       0.95</span><br><span class="line">Speed: 0.2ms preprocess, 1.6ms inference, 0.0ms loss, 1.0ms postprocess per image</span><br><span class="line">Results saved to D:\PythonWorkspace\yolov8-tutorial\runs\detect\my_model</span><br><span class="line">訓練完成，總耗時：0 時 4 分 1 秒</span><br><span class="line"></span><br><span class="line">Process finished with <span class="built_in">exit</span> code 0</span><br></pre></td></tr></table></figure><blockquote><p>💡 訓練完成後，結果會自動儲存在 <code>runs/detect/my_model/</code> 目錄下。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/yolov8-training/yolov8_training_log_summary.svg" alt="YOLOv8 訓練 Log 重點解析 ─ 環境資訊、模型規格、訓練參數與過程總覽"><br><em>圖：YOLOv8 訓練 Log 重點解析 ─ 環境資訊、模型規格、訓練參數與過程總覽</em></p><p><img loading="lazy" src="/images/python/opencv/yolov8-training/yolov8_cache_explanation.svg" alt="YOLOv8 train.cache 與 val.cache 說明 ─ 加速訓練與注意事項"><br><em>圖：YOLOv8 train.cache 與 val.cache 說明 ─ 加速訓練與注意事項</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><h2 id="🧠-重要訓練參數說明"><a href="#🧠-重要訓練參數說明" class="headerlink" title="🧠 重要訓練參數說明"></a>🧠 重要訓練參數說明</h2><p><img loading="lazy" src="/images/python/opencv/yolov8-training/yolov8_train_params.svg" alt="YOLOv8 train 完整參數表 ─ 16 個常用參數與預設值說明"><br><em>圖：YOLOv8 train 完整參數表 ─ 16 個常用參數與預設值說明</em></p><h3 id="💡-模型選擇建議"><a href="#💡-模型選擇建議" class="headerlink" title="💡 模型選擇建議"></a>💡 模型選擇建議</h3><p><img loading="lazy" src="/images/python/opencv/yolov8-training/yolov8_starting_modes.svg" alt="YOLOv8 三種起點模型選擇 ─ 從預訓練權重微調、從頭訓練到繼續中斷的 checkpoint"><br><em>圖：YOLOv8 三種起點模型選擇 ─ 從預訓練權重微調、從頭訓練到繼續中斷的 checkpoint</em></p><p><strong>① 從預訓練權重微調（一般情況，推薦）</strong><br>載入 Ultralytics 在 COCO 資料集上訓練好的權重，再用自己的資料繼續訓練。模型已具備基本的影像辨識能力，收斂快、所需資料量少，是最常見的做法。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># demo_pretrained.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)  <span class="comment"># 第一次執行會自動下載，約 6MB</span></span><br></pre></td></tr></table></figure><p><strong>② 從頭訓練（特殊情況）</strong><br>只載入網路架構定義，權重全部隨機初始化，需要大量資料（通常數千張以上）才能訓練出好的模型。若你的類別與 COCO 差異極大（例如醫療影像、衛星圖）才考慮。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># demo_from_scratch.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line">model = YOLO(<span class="string">"yolov8n.yaml"</span>)  <span class="comment"># 只有架構，無預訓練權重</span></span><br></pre></td></tr></table></figure><p><strong>③ 繼續訓練中斷的 checkpoint</strong><br>訓練途中若因斷電、關機等原因中斷，可從上次儲存的 <code>last.pt</code> 繼續，不需要從頭跑。詳細用法見本篇 [中斷後繼續訓練] 章節。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># demo_resume.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line">model = YOLO(<span class="string">"runs/detect/my_model/weights/last.pt"</span>)</span><br></pre></td></tr></table></figure><h2 id="💻-多張-GPU-訓練"><a href="#💻-多張-GPU-訓練" class="headerlink" title="💻 多張 GPU 訓練"></a>💻 多張 GPU 訓練</h2><p><code>device</code> 參數的常用值：<code>0</code>（第一張 GPU）、<code>&quot;0,1&quot;</code>（雙 GPU）、<code>&quot;cpu&quot;</code>（強制使用 CPU）。<br>有多張 GPU 時，可用以下方式自動偵測並全部啟用：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># setup_gpu.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">gpu_count = torch.cuda.device_count()</span><br><span class="line">device = <span class="string">","</span>.join(str(i) <span class="keyword">for</span> i <span class="keyword">in</span> range(gpu_count)) <span class="keyword">if</span> gpu_count &gt;= <span class="number">2</span> <span class="keyword">else</span> <span class="number">0</span></span><br><span class="line">print(<span class="string">f"偵測到 <span class="subst">&#123;gpu_count&#125;</span> 張 GPU，使用 device=<span class="subst">&#123;device!r&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line">model.train(data=<span class="string">"data.yaml"</span>, epochs=<span class="number">100</span>, device=device)</span><br></pre></td></tr></table></figure><h3 id="⛃-GPU-記憶體不足（OOM）"><a href="#⛃-GPU-記憶體不足（OOM）" class="headerlink" title="⛃ GPU 記憶體不足（OOM）"></a>⛃ GPU 記憶體不足（OOM）</h3><p><img loading="lazy" src="/images/python/opencv/yolov8-training/gpu_memory_batch_imgsz.svg" alt="GPU 記憶體使用原理 ─ batch 和 imgsz 是最吃記憶體的兩個參數"><br><em>圖：GPU 記憶體使用原理 ─ batch 和 imgsz 是最吃記憶體的兩個參數</em></p><p>訓練時 GPU 需要把圖片、模型權重、梯度全部同時放進記憶體計算。<br><code>batch</code>（每次丟幾張圖）和 <code>imgsz</code>（每張圖的解析度）是最吃記憶體的兩個設定：</p><ul><li><code>batch=16</code> 表示每次同時處理 16 張圖，16 張的資料都要同時放進 GPU</li><li><code>imgsz=640</code> 表示每張圖縮放到 640×640，解析度越高，每張圖佔的記憶體越大</li></ul><p>兩個值相乘就是每次計算的「記憶體負擔」。以 8GB 顯卡為例：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><ul><li><code>batch=16, imgsz=640</code> → 可能剛好 OOM</li><li><code>batch=8, imgsz=640</code> → 記憶體砍半，通常能跑</li><li><code>batch=8, imgsz=416</code> → 更小，適合記憶體非常有限的環境</li></ul><p>訓練啟動後若出現以下錯誤，就是記憶體不夠：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">RuntimeError: CUDA out of memory.</span><br></pre></td></tr></table></figure><p>依序嘗試以下調整，直到不再 OOM：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fix_oom.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法一：先縮小 batch（優先嘗試，對準確度影響較小）</span></span><br><span class="line">model.train(data=<span class="string">"data.yaml"</span>, epochs=<span class="number">100</span>, imgsz=<span class="number">640</span>, batch=<span class="number">8</span>,</span><br><span class="line">            device=<span class="number">0</span> <span class="keyword">if</span> torch.cuda.is_available() <span class="keyword">else</span> <span class="string">"cpu"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法二：batch 和 imgsz 同時縮小（記憶體非常有限時）</span></span><br><span class="line"><span class="comment"># model.train(data="data.yaml", epochs=100, imgsz=416, batch=8,</span></span><br><span class="line"><span class="comment">#             device=0 if torch.cuda.is_available() else "cpu")</span></span><br></pre></td></tr></table></figure><h2 id="💻-中斷後繼續訓練"><a href="#💻-中斷後繼續訓練" class="headerlink" title="💻 中斷後繼續訓練"></a>💻 中斷後繼續訓練</h2><p>YOLOv8 每個 epoch 都會儲存 <code>last.pt</code>，可隨時從中斷點繼續：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># resume_training.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法一：Python API</span></span><br><span class="line">model = YOLO(<span class="string">"runs/detect/my_model/weights/last.pt"</span>)</span><br><span class="line">model.train(resume=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法二：指令列</span></span><br><span class="line"><span class="comment"># yolo detect train resume model=runs/detect/my_model/weights/last.pt</span></span><br></pre></td></tr></table></figure><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>CPU 訓練速度非常慢</strong>：無 GPU 環境下，100 epochs 可能需要數小時甚至更長。第一次執行建議先將 <code>epochs</code> 設為 3~5，確認資料集路徑、格式、流程都正確後，再調回正式輪數。若需要完整訓練，強烈建議使用有 CUDA 的環境或 Google Colab（免費 GPU）。</li><li><strong>Windows 上 <code>workers</code> 需設為 0</strong>：設成其他數值可能造成 DataLoader 死鎖，訓練無法啟動。</li><li><strong><code>batch=-1</code> 自動計算</strong>：可設定 <code>batch=-1</code> 讓 YOLOv8 根據 GPU 記憶體自動計算最大 batch size，但不夠穩定，建議手動設定。</li><li><strong>訓練過程中不要刪除 <code>runs/</code> 目錄</strong>：中斷恢復需要依賴其中的 <code>last.pt</code> 與 <code>args.yaml</code>。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>YOLOv8 的基本訓練流程相當精簡，幾行程式碼就能啟動，並內建快取、自動儲存與斷點恢復等機制。<br>下一步是 <a href="/python-opencv-20260409-python-opencv-yolov8-training-advanced"><strong>YOLOv8 訓練進階設定</strong></a>，介紹資料增強與防止過擬合的進階參數調整。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/train/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Train</a><br><a href="https://docs.ultralytics.com/usage/cfg/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Configuration</a>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​​​​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌​​‌‌‌</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260408-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 預標籤（Pre-Label）</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260408-python-opencv-yolov8-pre-label/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260408-python-opencv-yolov8-pre-label/</id>
    <published>2026-04-08T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260407-python-opencv-yolov8-dataset"><strong>YOLOv8 資料集準備</strong></a> 中，我們完成了圖片蒐集、標註與資料集切分。<br>手動標記雖然精確，但對大量圖片來說非常耗時。</p><p>如果你要標記的類別（例如 <code>cat</code>、<code>dog</code>）剛好在 YOLOv8 預訓練模型支援的 COCO 80 類別內，可以先讓模型跑一遍推論，<strong>自動產生標籤草稿</strong>，再用 LabelImg 只修正錯誤的框，速度可以快上數倍。</p><h2 id="🔎-預標籤的原理與流程"><a href="#🔎-預標籤的原理與流程" class="headerlink" title="🔎 預標籤的原理與流程"></a>🔎 預標籤的原理與流程</h2><p>YOLOv8 的 COCO 預訓練模型已經學會辨識 <strong>80 種常見物件</strong>（包含 cat、dog）。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p><h3 id="🔸-如何確認-yolov8n-pt-是用-COCO-訓練的？只有-80-類？"><a href="#🔸-如何確認-yolov8n-pt-是用-COCO-訓練的？只有-80-類？" class="headerlink" title="🔸 如何確認 yolov8n.pt 是用 COCO 訓練的？只有 80 類？"></a>🔸 如何確認 yolov8n.pt 是用 COCO 訓練的？只有 80 類？</h3><p>你可以執行以下程式碼來驗證：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># check_coco_classes.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line">print(<span class="string">"類別總數："</span>, len(model.names))</span><br><span class="line">print(<span class="string">"類別對照表："</span>, model.names)</span><br></pre></td></tr></table></figure><p>執行後你會看到：</p><ul><li>類別總數為 <strong>80</strong></li><li>類別名稱包含 <code>person</code>、<code>car</code>、<code>cat</code>、<code>dog</code> 等常見物件</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">類別總數： 80</span><br><span class="line">類別對照表： &#123;</span><br><span class="line">0: <span class="string">'person'</span>, 1: <span class="string">'bicycle'</span>, 2: <span class="string">'car'</span>, 3: <span class="string">'motorcycle'</span>, 4: <span class="string">'airplane'</span>, 5: <span class="string">'bus'</span>, </span><br><span class="line">6: <span class="string">'train'</span>, 7: <span class="string">'truck'</span>, 8: <span class="string">'boat'</span>, 9: <span class="string">'traffic light'</span>, 10: <span class="string">'fire hydrant'</span>, </span><br><span class="line">11: <span class="string">'stop sign'</span>, 12: <span class="string">'parking meter'</span>, 13: <span class="string">'bench'</span>, 14: <span class="string">'bird'</span>, 15: <span class="string">'cat'</span>, </span><br><span class="line">16: <span class="string">'dog'</span>, 17: <span class="string">'horse'</span>, 18: <span class="string">'sheep'</span>, 19: <span class="string">'cow'</span>, 20: <span class="string">'elephant'</span>, </span><br><span class="line">21: <span class="string">'bear'</span>, 22: <span class="string">'zebra'</span>, 23: <span class="string">'giraffe'</span>, 24: <span class="string">'backpack'</span>, 25: <span class="string">'umbrella'</span>, </span><br><span class="line">26: <span class="string">'handbag'</span>, 27: <span class="string">'tie'</span>, 28: <span class="string">'suitcase'</span>, 29: <span class="string">'frisbee'</span>, 30: <span class="string">'skis'</span>, </span><br><span class="line">31: <span class="string">'snowboard'</span>, 32: <span class="string">'sports ball'</span>, 33: <span class="string">'kite'</span>, 34: <span class="string">'baseball bat'</span>, 35: <span class="string">'baseball glove'</span>, </span><br><span class="line">36: <span class="string">'skateboard'</span>, 37: <span class="string">'surfboard'</span>, 38: <span class="string">'tennis racket'</span>, 39: <span class="string">'bottle'</span>, 40: <span class="string">'wine glass'</span>, </span><br><span class="line">41: <span class="string">'cup'</span>, 42: <span class="string">'fork'</span>, 43: <span class="string">'knife'</span>, 44: <span class="string">'spoon'</span>, 45: <span class="string">'bowl'</span>, </span><br><span class="line">46: <span class="string">'banana'</span>, 47: <span class="string">'apple'</span>, 48: <span class="string">'sandwich'</span>, 49: <span class="string">'orange'</span>, 50: <span class="string">'broccoli'</span>, </span><br><span class="line">51: <span class="string">'carrot'</span>, 52: <span class="string">'hot dog'</span>, 53: <span class="string">'pizza'</span>, 54: <span class="string">'donut'</span>, 55: <span class="string">'cake'</span>, </span><br><span class="line">56: <span class="string">'chair'</span>, 57: <span class="string">'couch'</span>, 58: <span class="string">'potted plant'</span>, 59: <span class="string">'bed'</span>, 60: <span class="string">'dining table'</span>, </span><br><span class="line">61: <span class="string">'toilet'</span>, 62: <span class="string">'tv'</span>, 63: <span class="string">'laptop'</span>, 64: <span class="string">'mouse'</span>, 65: <span class="string">'remote'</span>, </span><br><span class="line">66: <span class="string">'keyboard'</span>, 67: <span class="string">'cell phone'</span>, 68: <span class="string">'microwave'</span>, 69: <span class="string">'oven'</span>, 70: <span class="string">'toaster'</span>, </span><br><span class="line">71: <span class="string">'sink'</span>, 72: <span class="string">'refrigerator'</span>, 73: <span class="string">'book'</span>, 74: <span class="string">'clock'</span>, 75: <span class="string">'vase'</span>, </span><br><span class="line">76: <span class="string">'scissors'</span>, 77: <span class="string">'teddy bear'</span>, 78: <span class="string">'hair drier'</span>, 79: <span class="string">'toothbrush'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>這就是官方 COCO 資料集的 80 個類別。因此只有這些類別才能使用預標籤功能。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p><h3 id="🔸-原理說明"><a href="#🔸-原理說明" class="headerlink" title="🔸 原理說明"></a>🔸 原理說明</h3><p>YOLOv8 的 COCO 預訓練模型能偵測 80 種物件，偵測結果包含：</p><ul><li>類別 ID（對應 COCO 80 類）</li><li>邊界框座標（已是相對比例，可直接寫入 YOLO txt）</li></ul><p><img loading="lazy" src="/images/python/opencv/yolov8-pre-label/pre_label_flow.svg" alt="pre_label.py 自動預標籤流程 ─ 用 YOLOv8 預訓練模型快速產生初始標註的 6 個步驟"><br><em>圖：pre_label.py 自動預標籤流程 ─ 用 YOLOv8 預訓練模型快速產生初始標註的 6 個步驟</em></p><blockquote><p>💡 COCO 資料集中 <code>cat</code> 的 class_id 是 15，<code>dog</code> 是 16，與我們 <code>data.yaml</code> 定義的 0、1 不同，必須重新對應，否則 class_id 會錯。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p></blockquote><h2 id="🧠-函式與參數說明"><a href="#🧠-函式與參數說明" class="headerlink" title="🧠 函式與參數說明"></a>🧠 函式與參數說明</h2><p><img loading="lazy" src="/images/python/opencv/yolov8-pre-label/yolov8_box_xywhn_conf.svg" alt="YOLOv8 偵測結果重要屬性 ─ box.xywhn（正規化座標）與 conf（信心度門檻）說明"><br><em>圖：YOLOv8 偵測結果重要屬性 ─ box.xywhn（正規化座標）與 conf（信心度門檻）說明</em></p><h2 id="💻-範例程式"><a href="#💻-範例程式" class="headerlink" title="💻 範例程式"></a>💻 範例程式</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># pre_label.py</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="comment"># ── 設定 ────────────────────────────────────</span></span><br><span class="line">model_path     = <span class="string">"yolov8n.pt"</span>   <span class="comment"># 預訓練模型，第一次執行會自動下載</span></span><br><span class="line">src_dir        = <span class="string">"raw_images"</span>   <span class="comment"># 原始圖片目錄（與 20260407 的 download_images.py 輸出一致）</span></span><br><span class="line">dst_dir        = <span class="string">"raw_labels"</span>   <span class="comment"># 標籤輸出目錄</span></span><br><span class="line">conf_threshold = <span class="number">0.5</span>            <span class="comment"># 信心度門檻，低於此值的偵測結果會被過濾</span></span><br><span class="line">target_classes = &#123;<span class="string">"cat"</span>: <span class="number">0</span>, <span class="string">"dog"</span>: <span class="number">1</span>&#125;  <span class="comment"># 只保留這些類別，並重新對應為 0-based class_id</span></span><br><span class="line"><span class="comment"># ────────────────────────────────────────────</span></span><br><span class="line"></span><br><span class="line">model      = YOLO(model_path)</span><br><span class="line">coco_names = model.names  <span class="comment"># COCO 80 類別名稱對照表（dict: id → name）</span></span><br><span class="line"></span><br><span class="line">os.makedirs(dst_dir, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 產生 classes.txt，LabelImg 需要此檔案才能正確對應類別名稱</span></span><br><span class="line">classes_txt = os.path.join(dst_dir, <span class="string">"classes.txt"</span>)</span><br><span class="line"><span class="keyword">with</span> open(classes_txt, <span class="string">"w"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    <span class="keyword">for</span> name <span class="keyword">in</span> sorted(target_classes, key=target_classes.get):</span><br><span class="line">        f.write(name + <span class="string">"\n"</span>)</span><br><span class="line"></span><br><span class="line">img_files = [f <span class="keyword">for</span> f <span class="keyword">in</span> os.listdir(src_dir)</span><br><span class="line">             <span class="keyword">if</span> f.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".jpeg"</span>, <span class="string">".png"</span>))]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> fname <span class="keyword">in</span> img_files:</span><br><span class="line">    img_path = os.path.join(src_dir, fname)</span><br><span class="line">    stem     = os.path.splitext(fname)[<span class="number">0</span>]</span><br><span class="line">    lbl_path = os.path.join(dst_dir, stem + <span class="string">".txt"</span>)</span><br><span class="line"></span><br><span class="line">    results = model(img_path, conf=conf_threshold, verbose=<span class="literal">False</span>)[<span class="number">0</span>]</span><br><span class="line">    lines   = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> box <span class="keyword">in</span> results.boxes:</span><br><span class="line">        coco_cls_id = int(box.cls)</span><br><span class="line">        coco_name   = coco_names[coco_cls_id]</span><br><span class="line">        <span class="keyword">if</span> coco_name <span class="keyword">not</span> <span class="keyword">in</span> target_classes:</span><br><span class="line">            <span class="keyword">continue</span>  <span class="comment"># 過濾不需要的類別</span></span><br><span class="line"></span><br><span class="line">        new_cls_id     = target_classes[coco_name]</span><br><span class="line">        x, y, w, h     = box.xywhn[<span class="number">0</span>].tolist()  <span class="comment"># 已是相對比例，直接使用</span></span><br><span class="line">        lines.append(<span class="string">f"<span class="subst">&#123;new_cls_id&#125;</span> <span class="subst">&#123;x:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;y:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;w:<span class="number">.6</span>f&#125;</span> <span class="subst">&#123;h:<span class="number">.6</span>f&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> open(lbl_path, <span class="string">"w"</span>) <span class="keyword">as</span> f:</span><br><span class="line">        f.write(<span class="string">"\n"</span>.join(lines))</span><br><span class="line"></span><br><span class="line">    status = <span class="string">f"<span class="subst">&#123;len(lines)&#125;</span> 個標籤"</span> <span class="keyword">if</span> lines <span class="keyword">else</span> <span class="string">"無偵測結果（空標籤）"</span></span><br><span class="line">    print(<span class="string">f"<span class="subst">&#123;fname&#125;</span>: <span class="subst">&#123;status&#125;</span>"</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"\n完成：共處理 <span class="subst">&#123;len(img_files)&#125;</span> 張圖片，標籤輸出至 <span class="subst">&#123;dst_dir&#125;</span>/"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-pre-label/01.gif" alt="對 raw_images/ 批次推論並輸出 YOLO txt 標籤至 raw_labels/，顯示每張圖片的偵測數量"><br><em>圖：對 raw_images/ 批次推論並輸出 YOLO txt 標籤至 raw_labels/，顯示每張圖片的偵測數量</em></p><h2 id="💻-用-LabelImg-修正標籤"><a href="#💻-用-LabelImg-修正標籤" class="headerlink" title="💻 用 LabelImg 修正標籤"></a>💻 用 LabelImg 修正標籤</h2><p>執行完 <code>pre_label.py</code> 後，<code>raw_labels/</code> 已有自動產生的標籤草稿。接著用 LabelImg 開啟修正：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p><ol><li><strong>Open Dir</strong>：選擇 <code>raw_images/</code></li><li><strong>Change Save Dir</strong>：選擇 <code>raw_labels/</code>（LabelImg 會自動載入同名的 <code>.txt</code>）</li><li><strong>儲存格式</strong>切換為 <strong>YOLO</strong></li><li>逐張檢查，刪除錯誤的框、補畫漏標的物件、修正類別</li><li><code>Ctrl + S</code> 儲存後按 <code>D</code> 切到下一張</li></ol><p><img loading="lazy" src="/images/python/opencv/yolov8-pre-label/02.gif" alt="在 LabelImg 中載入自動產生的標籤草稿，修正偵測錯誤的邊界框"><br><em>圖：在 LabelImg 中載入自動產生的標籤草稿，修正偵測錯誤的邊界框</em></p><blockquote><p>💡 <code>pre_label.py</code> 執行時會自動產生 <code>raw_labels/classes.txt</code>，LabelImg 需要此檔案才能正確對應類別名稱，不需要手動建立。</p></blockquote><h2 id="🔄-後續步驟：完成資料集準備"><a href="#🔄-後續步驟：完成資料集準備" class="headerlink" title="🔄 後續步驟：完成資料集準備"></a>🔄 後續步驟：完成資料集準備</h2><p>預標籤只是取代了 <a href="/python-opencv-20260407-python-opencv-yolov8-dataset"><strong>YOLOv8 資料集準備</strong></a> 中的「步驟 2：手動標註」，完整的資料集流程到這裡還剩三個步驟尚未完成：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p><table><thead><tr><th>步驟</th><th>腳本</th><th>說明</th></tr></thead><tbody><tr><td>步驟 3</td><td><code>split_dataset.py</code></td><td>將 <code>raw_images/</code> 與 <code>raw_labels/</code> 隨機分割為訓練集（80%）與驗證集（20%）</td></tr><tr><td>步驟 4</td><td><code>create_yaml.py</code></td><td>產生 <code>data.yaml</code>，告訴 YOLOv8 資料集路徑與類別定義</td></tr><tr><td>步驟 5</td><td><code>verify_dataset.py</code></td><td>驗證所有圖片與標籤的格式、對應關係、class_id 範圍</td></tr></tbody></table><p>這三個腳本的完整程式碼與說明請參考 <a href="/python-opencv-20260407-python-opencv-yolov8-dataset"><strong>YOLOv8 資料集準備</strong></a> 的步驟 3～5。</p><blockquote><p>⚠️ 省略驗證步驟直接訓練，是訓練時「資料集格式錯誤」或「一直用舊資料」的最常見原因。</p></blockquote><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>只適用於 COCO 80 類別內的物件</strong>：若你要標記的類別不在 COCO 80 類別內（例如特定商品、自訂符號），預標籤無法使用，只能全手動或先用少量資料訓練一個初版模型再回頭預標。</li><li><strong>預標籤不等於正確標籤</strong>：模型可能漏標、誤標，每張圖都必須人工確認，不能直接用於訓練。</li><li><strong>空標籤檔案需保留</strong>：模型未偵測到目標的圖片，<code>pre_label.py</code> 會輸出空的 <code>.txt</code>，這是正確的 YOLO 格式，不要刪除。</li><li><strong>信心度門檻影響標籤品質</strong>：<code>conf</code> 設太低會產生大量誤標框，設太高會漏掉部分真實物件，建議先用 0.5 再依實際結果調整。</li></ul><h2 id="📊-應用場景"><a href="#📊-應用場景" class="headerlink" title="📊 應用場景"></a>📊 應用場景</h2><ul><li><strong>大量圖片需要標記</strong>：100 張以上的資料集，預標籤可以省去大部分畫框時間，只需修正。</li><li><strong>新類別自訓練的冷啟動</strong>：先用 COCO 預標籤標出相近類別（例如用 <code>person</code> 預標人物），修正後訓練初版模型，再用初版模型回頭預標更多資料，形成正向循環。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>預標籤不是讓你省掉標記，而是把「從零畫框」變成「改錯」，後者快得多。<br>完成標籤修正後，別忘了回到 <a href="/python-opencv-20260407-python-opencv-yolov8-dataset"><strong>YOLOv8 資料集準備</strong></a> 完成剩餘三個步驟：用 <code>split_dataset.py</code> 切分資料集、用 <code>create_yaml.py</code> 產生 <code>data.yaml</code>、再用 <code>verify_dataset.py</code> 驗證格式無誤，才算真正備妥訓練資料。<br>下一步是 <a href="/python-opencv-20260409-python-opencv-yolov8-training"><strong>YOLOv8 模型訓練</strong></a>，正式把資料送入模型開始學習。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌​​‌​​‌‌​​‌​‌​​‌​‌‌​‌​‌‌​‌‌​​​‌‌​​​​‌​‌‌​​​‌​​‌‌​​‌​‌​‌‌​‌‌​​</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/modes/predict/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Predict</a><br><a href="https://docs.ultralytics.com/reference/engine/results/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Results</a><br><a href="https://cocodataset.org/#explore" target="_blank" rel="external nofollow noopener noreferrer">COCO Dataset — Categories</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260407-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 資料集準備</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260407-python-opencv-yolov8-dataset/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260407-python-opencv-yolov8-dataset/</id>
    <published>2026-04-07T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260406-python-opencv-yolov8-install"><strong>YOLOv8 介紹與環境安裝</strong></a> 中，我們完成了環境設定並測試了預訓練模型。<br>這一篇進入自訓練的第一步：<strong>準備自己的資料集</strong>。</p><p>YOLOv8 對資料集的格式要求非常嚴格，如果格式錯誤，訓練時就會直接報錯。因此我們必須一步一步按照正確流程來做。</p><p>完整流程如下：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/yolov8_data_preparation_flow.svg" alt="Python - 圖 1 (yolov8 data preparation flow)"></p><p>走完這些步驟後，你的專案目錄會變成這樣：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">project&#x2F;</span><br><span class="line">├── raw_images&#x2F;          ← 蒐集的原始圖片</span><br><span class="line">├── raw_labels&#x2F;          ← LabelImg 標註輸出</span><br><span class="line">├── dataset&#x2F;             ← split_dataset.py 切分後的訓練資料</span><br><span class="line">│   ├── images&#x2F;</span><br><span class="line">│   │   ├── train&#x2F;</span><br><span class="line">│   │   └── val&#x2F;</span><br><span class="line">│   └── labels&#x2F;</span><br><span class="line">│       ├── train&#x2F;</span><br><span class="line">│       └── val&#x2F;</span><br><span class="line">├── data.yaml            ← create_yaml.py 產生</span><br><span class="line">├── download_images.py</span><br><span class="line">├── split_dataset.py</span><br><span class="line">├── create_yaml.py</span><br><span class="line">└── verify_dataset.py</span><br></pre></td></tr></table></figure><h2 id="🗃️-步驟-1：蒐集圖片"><a href="#🗃️-步驟-1：蒐集圖片" class="headerlink" title="🗃️ 步驟 1：蒐集圖片"></a>🗃️ 步驟 1：蒐集圖片</h2><p>與分類任務不同，物件偵測的原始圖片<strong>不需要分類別放進不同資料夾</strong>，全部放在同一個資料夾即可。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><p>以下是使用 Bing 搜尋引擎批量下載圖片的範例：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># download_images.py</span></span><br><span class="line"><span class="keyword">from</span> icrawler.builtin <span class="keyword">import</span> BingImageCrawler</span><br><span class="line"></span><br><span class="line">keywords = [<span class="string">"cat"</span>, <span class="string">"dog"</span>]   <span class="comment"># 你想要偵測的類別</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> kw <span class="keyword">in</span> keywords:</span><br><span class="line">    crawler = BingImageCrawler(storage=&#123;<span class="string">"root_dir"</span>: <span class="string">"raw_images"</span>&#125;)</span><br><span class="line">    crawler.crawl(keyword=kw, max_num=<span class="number">200</span>)   <span class="comment"># 建議每類至少 100~200 張</span></span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/01.png" alt="使用 BingImageCrawler 批次下載 cat 與 dog 圖片至 raw_images 目錄"><br><em>圖：使用 BingImageCrawler 批次下載 cat 與 dog 圖片至 raw_images 目錄</em></p><blockquote><p>⚠️ 注意版權問題，建議僅用於研究與學習目的。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p></blockquote><h2 id="💻-步驟-2：使用-LabelImg-進行標註"><a href="#💻-步驟-2：使用-LabelImg-進行標註" class="headerlink" title="💻 步驟 2：使用 LabelImg 進行標註"></a>💻 步驟 2：使用 LabelImg 進行標註</h2><p>LabelImg 的完整安裝、操作步驟與已知的 PyQt5 崩潰修法，已在 <a href="/python-opencv-20260325-python-opencv-labelimg"><strong>LabelImg 標註工具實戰</strong></a> 篇完整說明，這裡只補充 YOLOv8 工作流程中特有的設定：</p><p>標註時需要注意以下重點：</p><ul><li><strong>Open Dir</strong>：選擇 <code>raw_images/</code> 資料夾</li><li><strong>Change Save Dir</strong>：選擇 <code>raw_labels/</code> 資料夾（建議先分開存放）</li><li><strong>儲存格式</strong>：務必切換成 <strong>YOLO</strong> 格式（不是 Pascal VOC）</li><li>每張圖片標註完後，會產生一個同名的 <code>.txt</code> 檔案</li></ul><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/02.png" alt="在 LabelImg 中設定 Open Dir 為 raw_images/、Change Save Dir 為 raw_labels/，並切換儲存格式為 YOLO"><br><em>圖：在 LabelImg 中設定 Open Dir 為 raw_images/、Change Save Dir 為 raw_labels/，並切換儲存格式為 YOLO</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><blockquote><p>💡 若想減少手動標記的工作量，可先參考 <a href="/python-opencv-20260408-python-opencv-yolov8-pre-label"><strong>YOLOv8 預標籤</strong></a> 篇，讓模型自動產生標籤草稿，再用 LabelImg 修正。</p></blockquote><h2 id="✂️-步驟-3：自動分割訓練集與驗證集"><a href="#✂️-步驟-3：自動分割訓練集與驗證集" class="headerlink" title="✂️ 步驟 3：自動分割訓練集與驗證集"></a>✂️ 步驟 3：自動分割訓練集與驗證集</h2><p>標註完成後，使用以下腳本自動切分資料（推薦 80% 訓練集、20% 驗證集）：<br>YOLOv8 要求的結構，需要的是 <code>images/</code> 和 <code>labels/</code> 分開的結構，以下是對應的版本：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">dataset&#x2F;</span><br><span class="line">├── images&#x2F;</span><br><span class="line">│   ├── train&#x2F;          ← 訓練集圖片</span><br><span class="line">│   │   ├── 0001.jpg</span><br><span class="line">│   │   └── ...</span><br><span class="line">│   └── val&#x2F;            ← 驗證集圖片</span><br><span class="line">│       ├── 0101.jpg</span><br><span class="line">│       └── ...</span><br><span class="line">└── labels&#x2F;</span><br><span class="line">    ├── train&#x2F;          ← 訓練集標籤（與 images&#x2F;train&#x2F; 一一對應）</span><br><span class="line">    │   ├── 0001.txt</span><br><span class="line">    │   └── ...</span><br><span class="line">    └── val&#x2F;            ← 驗證集標籤（與 images&#x2F;val&#x2F; 一一對應）</span><br><span class="line">        ├── 0101.txt</span><br><span class="line">        └── ...</span><br></pre></td></tr></table></figure><p><strong>重要規則：</strong>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><ul><li><code>images/</code> 與 <code>labels/</code> 的子目錄結構必須<strong>完全對應</strong></li><li>圖片檔名與標籤檔名必須相同（只有副檔名不同）</li><li>例如 <code>images/train/0001.jpg</code> → <code>labels/train/0001.txt</code></li><li>訓練集與驗證集的比例建議為 <strong>80% / 20%</strong></li></ul><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># split_dataset.py</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line">src_images = <span class="string">"raw_images"</span>       <span class="comment"># 原始圖片目錄</span></span><br><span class="line">src_labels = <span class="string">"raw_labels"</span>       <span class="comment"># 原始標籤目錄</span></span><br><span class="line">dst_root   = <span class="string">"dataset"</span>          <span class="comment"># 輸出目錄</span></span><br><span class="line">val_ratio  = <span class="number">0.2</span>                <span class="comment"># 驗證集比例</span></span><br><span class="line">random.seed(<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> split <span class="keyword">in</span> [<span class="string">"train"</span>, <span class="string">"val"</span>]:</span><br><span class="line">    os.makedirs(<span class="string">f"<span class="subst">&#123;dst_root&#125;</span>/images/<span class="subst">&#123;split&#125;</span>"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line">    os.makedirs(<span class="string">f"<span class="subst">&#123;dst_root&#125;</span>/labels/<span class="subst">&#123;split&#125;</span>"</span>, exist_ok=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">files = [f <span class="keyword">for</span> f <span class="keyword">in</span> os.listdir(src_images)</span><br><span class="line">         <span class="keyword">if</span> f.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".jpeg"</span>, <span class="string">".png"</span>))]</span><br><span class="line">random.shuffle(files)</span><br><span class="line"></span><br><span class="line">val_count = int(len(files) * val_ratio)</span><br><span class="line">val_files = set(files[:val_count])</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> fname <span class="keyword">in</span> files:</span><br><span class="line">    split    = <span class="string">"val"</span> <span class="keyword">if</span> fname <span class="keyword">in</span> val_files <span class="keyword">else</span> <span class="string">"train"</span></span><br><span class="line">    stem     = os.path.splitext(fname)[<span class="number">0</span>]</span><br><span class="line">    label_fn = stem + <span class="string">".txt"</span></span><br><span class="line"></span><br><span class="line">    shutil.copy(</span><br><span class="line">        os.path.join(src_images, fname),</span><br><span class="line">        os.path.join(dst_root, <span class="string">"images"</span>, split, fname)</span><br><span class="line">    )</span><br><span class="line">    label_src = os.path.join(src_labels, label_fn)</span><br><span class="line">    <span class="keyword">if</span> os.path.exists(label_src):</span><br><span class="line">        shutil.copy(label_src,</span><br><span class="line">                    os.path.join(dst_root, <span class="string">"labels"</span>, split, label_fn))</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># 無標註物件的圖片，建立空標籤檔</span></span><br><span class="line">        open(os.path.join(dst_root, <span class="string">"labels"</span>, split, label_fn), <span class="string">"w"</span>).close()</span><br><span class="line"></span><br><span class="line">print(<span class="string">f"完成：train=<span class="subst">&#123;len(files)-val_count&#125;</span> 張，val=<span class="subst">&#123;val_count&#125;</span> 張"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/03.png" alt="自動依比例將圖片與標籤隨機分割為訓練集與驗證集，並建立對應的目錄結構"><br><em>圖：自動依比例將圖片與標籤隨機分割為訓練集與驗證集，並建立對應的目錄結構</em></p><h2 id="🧠-步驟-4：產生-data-yaml-設定檔"><a href="#🧠-步驟-4：產生-data-yaml-設定檔" class="headerlink" title="🧠 步驟 4：產生 data.yaml 設定檔"></a>🧠 步驟 4：產生 data.yaml 設定檔</h2><p><code>data.yaml</code> 是告訴 YOLOv8 資料集位置與類別定義的設定檔，訓練時必須指定。跑完 <code>split_dataset.py</code> 後建立，放在專案根目錄（與 <code>dataset/</code> 同層）：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">project&#x2F;</span><br><span class="line">├── raw_images&#x2F;</span><br><span class="line">├── raw_labels&#x2F;</span><br><span class="line">├── dataset&#x2F;</span><br><span class="line">│   ├── images&#x2F;</span><br><span class="line">│   │   ├── train&#x2F;</span><br><span class="line">│   │   └── val&#x2F;</span><br><span class="line">│   └── labels&#x2F;</span><br><span class="line">│       ├── train&#x2F;</span><br><span class="line">│       └── val&#x2F;</span><br><span class="line">├── data.yaml            ← 放這裡</span><br><span class="line">├── create_yaml.py</span><br><span class="line">└── split_dataset.py</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># create_yaml.py</span></span><br><span class="line"><span class="keyword">import</span> yaml</span><br><span class="line"></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">"path"</span>: <span class="string">"./dataset"</span>,           <span class="comment"># 資料集根目錄</span></span><br><span class="line">    <span class="string">"train"</span>: <span class="string">"images/train"</span>,       <span class="comment"># 訓練集圖片相對路徑</span></span><br><span class="line">    <span class="string">"val"</span>: <span class="string">"images/val"</span>,           <span class="comment"># 驗證集圖片相對路徑</span></span><br><span class="line">    <span class="string">"nc"</span>: <span class="number">2</span>,                       <span class="comment"># 類別數量（cat 和 dog → 2）</span></span><br><span class="line">    <span class="string">"names"</span>: &#123;<span class="number">0</span>: <span class="string">"cat"</span>, <span class="number">1</span>: <span class="string">"dog"</span>&#125;  <span class="comment"># class_id 對應的類別名稱</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"data.yaml"</span>, <span class="string">"w"</span>, encoding=<span class="string">"utf-8"</span>) <span class="keyword">as</span> f:</span><br><span class="line">    yaml.dump(data, f, default_flow_style=<span class="literal">False</span>, allow_unicode=<span class="literal">True</span>, sort_keys=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">print(<span class="string">"data.yaml 已成功產生！"</span>)</span><br></pre></td></tr></table></figure><p>產生的 <code>data.yaml</code> 內容範例：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">path:</span> <span class="string">./dataset</span></span><br><span class="line"><span class="attr">train:</span> <span class="string">images/train</span></span><br><span class="line"><span class="attr">val:</span> <span class="string">images/val</span></span><br><span class="line"><span class="attr">nc:</span> <span class="number">2</span></span><br><span class="line"><span class="attr">names:</span></span><br><span class="line">  <span class="attr">0:</span> <span class="string">cat</span></span><br><span class="line">  <span class="attr">1:</span> <span class="string">dog</span></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>names</code> 的順序就是標註時的 class_id，必須與標籤檔案中的 ID 完全對應，不能錯位。</p></blockquote><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/04.png" alt="執行 create_yaml.py 自動產生 data.yaml 設定檔"><br><em>圖：執行 create_yaml.py 自動產生 data.yaml 設定檔</em></p><h2 id="💻-步驟-5：驗證資料集格式"><a href="#💻-步驟-5：驗證資料集格式" class="headerlink" title="💻 步驟 5：驗證資料集格式"></a>💻 步驟 5：驗證資料集格式</h2><p>在開始訓練前，務必執行驗證，避免格式錯誤導致訓練失敗：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># verify_dataset.py</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">verify_dataset</span><span class="params">(img_dir, lbl_dir, class_names)</span>:</span></span><br><span class="line">    errors = []</span><br><span class="line">    <span class="keyword">for</span> fname <span class="keyword">in</span> os.listdir(img_dir):</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> fname.lower().endswith((<span class="string">".jpg"</span>, <span class="string">".jpeg"</span>, <span class="string">".png"</span>)):</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        stem     = os.path.splitext(fname)[<span class="number">0</span>]</span><br><span class="line">        img_path = os.path.join(img_dir, fname)</span><br><span class="line">        lbl_path = os.path.join(lbl_dir, stem + <span class="string">".txt"</span>)</span><br><span class="line"></span><br><span class="line">        img = cv2.imread(img_path)</span><br><span class="line">        <span class="keyword">if</span> img <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            errors.append(<span class="string">f"圖片損壞：<span class="subst">&#123;img_path&#125;</span>"</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(lbl_path):</span><br><span class="line">            errors.append(<span class="string">f"缺少標籤：<span class="subst">&#123;lbl_path&#125;</span>"</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        h, w = img.shape[:<span class="number">2</span>]</span><br><span class="line">        <span class="keyword">with</span> open(lbl_path) <span class="keyword">as</span> f:</span><br><span class="line">            <span class="keyword">for</span> i, line <span class="keyword">in</span> enumerate(f):</span><br><span class="line">                parts = line.strip().split()</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> parts:</span><br><span class="line">                    <span class="keyword">continue</span>  <span class="comment"># 跳過空行（檔案結尾的換行符）</span></span><br><span class="line">                <span class="keyword">if</span> len(parts) != <span class="number">5</span>:</span><br><span class="line">                    errors.append(<span class="string">f"格式錯誤（<span class="subst">&#123;lbl_path&#125;</span> 第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>行）"</span>)</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                cls_id = int(parts[<span class="number">0</span>])</span><br><span class="line">                <span class="keyword">if</span> cls_id &gt;= len(class_names):</span><br><span class="line">                    errors.append(<span class="string">f"class_id 超出範圍（<span class="subst">&#123;lbl_path&#125;</span> 第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>行）"</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> errors:</span><br><span class="line">        print(<span class="string">f"發現 <span class="subst">&#123;len(errors)&#125;</span> 個問題："</span>)</span><br><span class="line">        <span class="keyword">for</span> e <span class="keyword">in</span> errors:</span><br><span class="line">            print(<span class="string">f"  ✗ <span class="subst">&#123;e&#125;</span>"</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        print(<span class="string">"✅ 資料集格式驗證通過"</span>)</span><br><span class="line"></span><br><span class="line">verify_dataset(<span class="string">"dataset/images/train"</span>, <span class="string">"dataset/labels/train"</span>,</span><br><span class="line">               class_names=[<span class="string">"cat"</span>, <span class="string">"dog"</span>])  <span class="comment"># 依實際類別修改，順序須與 data.yaml 的 names 一致</span></span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-dataset/05.png" alt="逐一檢查圖片與標籤的對應關係、格式正確性及 class_id 範圍，輸出驗證結果"><br><em>圖：逐一檢查圖片與標籤的對應關係、格式正確性及 class_id 範圍，輸出驗證結果</em></p><h2 id="✨-補充說明"><a href="#✨-補充說明" class="headerlink" title="✨ 補充說明"></a>✨ 補充說明</h2><p>YOLO txt 格式在 LabelImg 實際產出的座標是怎麼計算的，方便理解標籤檔的內容。</p><p>每張圖片對應一個同名 <code>.txt</code>，每行一個物件：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;class_id&gt; &lt;x_center&gt; &lt;y_center&gt; &lt;width&gt; &lt;height&gt;</span><br></pre></td></tr></table></figure><p>所有座標都是<strong>相對比例（0.0 ~ 1.0）</strong>，不是像素值。以一張 640×480 的圖片為例，貓（class_id=0）的邊界框左上角在 (100, 80)、右下角在 (300, 280)：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">x_center &#x3D; (100 + 300) &#x2F; 2 &#x2F; 640 &#x3D; 0.3125</span><br><span class="line">y_center &#x3D; (80  + 280) &#x2F; 2 &#x2F; 480 &#x3D; 0.375</span><br><span class="line">width    &#x3D; (300 - 100) &#x2F; 640     &#x3D; 0.3125</span><br><span class="line">height   &#x3D; (280 -  80) &#x2F; 480     &#x3D; 0.4167</span><br><span class="line"></span><br><span class="line"># 寫入 .txt 的內容</span><br><span class="line">0 0.3125 0.375 0.3125 0.4167</span><br></pre></td></tr></table></figure><p>若同一張圖片有多個物件，每行寫一個：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">0 0.3125 0.375 0.3125 0.4167</span><br><span class="line">1 0.7500 0.600 0.2000 0.3000</span><br></pre></td></tr></table></figure><blockquote><p>💡 若圖片中沒有任何標註物件，對應的 <code>.txt</code> 應為<strong>空檔案</strong>，而非不存在。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p></blockquote><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>類別 ID 從 0 開始</strong>：標籤檔案中的 class_id 必須從 0 開始，且不能有跳號。</li><li><strong>圖片與標籤一定要一一對應</strong>：若有圖片沒有對應的 <code>.txt</code>，YOLOv8 訓練時會報錯。</li><li><strong>座標必須是相對比例</strong>：絕對像素座標不是有效的 YOLO 格式，必須換算成 0.0~1.0 的比例值。</li><li><strong>建議圖片解析度一致</strong>：不同解析度的圖片混在一起沒有問題，YOLOv8 會自動 resize，但解析度差異太大可能影響訓練效果。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>資料集準備是 YOLOv8 自訓練最重要也最容易出錯的步驟。</p><p>只要確實按照「蒐集 → 標註 → 分割 → 產生 yaml → 驗證」的順序來做，並在最後一步用 <code>verify_dataset.py</code> 確認無誤，就可以放心進入訓練階段。</p><p>下一步是 <a href="/python-opencv-20260408-python-opencv-yolov8-pre-label"><strong>YOLOv8 預標籤（Pre-Label）</strong></a>，學習如何利用預訓練模型自動產生標籤草稿，減少手動標記的工作量。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌‌​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​​‌​​​‌‌​​​​‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌‌​​‌‌​‌‌​​‌​‌​‌‌‌​‌​​</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/datasets/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件 — Datasets</a><br><a href="https://github.com/HumanSignal/labelImg" target="_blank" rel="external nofollow noopener noreferrer">LabelImg GitHub</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260406-python-opencv-yolov8
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV YOLOv8 介紹與環境安裝</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260406-python-opencv-yolov8-install/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260406-python-opencv-yolov8-install/</id>
    <published>2026-04-06T10:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在 <a href="/python-opencv-20260406-python-opencv-yolov8-intro"><strong>從分類到偵測 — YOLOv8 系列介紹</strong></a> 中說明了分類與偵測任務的差異，以及這個子系列的學習路線。<br>如果你的目標是「<strong>在圖片中找出物件的位置</strong>」，也就是物件偵測任務，就需要不同的模型與訓練方式。</p><p><strong>YOLOv8</strong> 是目前最主流的即時物件偵測框架，由 Ultralytics 開發與維護。<br>這個系列文章將帶你從環境安裝開始，一路到用自己的資料集訓練出專屬的偵測模型。</p><h2 id="🔎-什麼是-YOLO？"><a href="#🔎-什麼是-YOLO？" class="headerlink" title="🔎 什麼是 YOLO？"></a>🔎 什麼是 YOLO？</h2><p>YOLO（You Only Look Once）是一系列即時物件偵測模型的統稱。<br>與傳統的兩階段偵測器（如 Faster R-CNN）不同，YOLO 只需一次前向傳播就能同時預測所有物件的位置與類別，速度極快。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p><p>YOLOv8 是第八代，由 Ultralytics 在 2023 年發布，相較前代有以下特點：</p><ul><li>更高的偵測準確度（mAP）</li><li>更快的推論速度</li><li>統一的 Python API，支援偵測、分割、分類、姿態估計等多種任務</li><li>內建訓練、驗證、推論、匯出等完整工具鏈</li></ul><h2 id="🧠-模型規格與選擇"><a href="#🧠-模型規格與選擇" class="headerlink" title="🧠 模型規格與選擇"></a>🧠 模型規格與選擇</h2><p><img loading="lazy" src="/images/python/opencv/yolov8-install/yolov8_model_comparison.svg" alt="YOLOv8 五種模型規格比較 ─ 從 nano 到 extra 的參數量、準確度、速度與推薦情境"><br><em>圖：YOLOv8 五種模型規格比較 ─ 從 nano 到 extra 的參數量、準確度、速度與推薦情境</em></p><blockquote><p>💡 建議先用 <code>yolov8n</code> 或 <code>yolov8s</code> 跑通訓練流程，確認資料沒問題後，再換較大的模型提升準確度。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p></blockquote><h2 id="🛠️-環境安裝"><a href="#🛠️-環境安裝" class="headerlink" title="🛠️ 環境安裝"></a>🛠️ 環境安裝</h2><p>本系列後續所有範例皆以 GPU 執行為主。</p><p>相關環境安裝文章：</p><ul><li><a href="/python-setup-20260103-python-cuda-cudnn">CUDA 與 cuDNN：用途與環境安裝</a></li><li><a href="/python-opencv-20260401-python-opencv-gpu-acceleration">GPU 加速與效能優化</a></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安裝 ultralytics</span></span><br><span class="line">pip install ultralytics</span><br></pre></td></tr></table></figure><h2 id="💻-驗證安裝"><a href="#💻-驗證安裝" class="headerlink" title="💻 驗證安裝"></a>💻 驗證安裝</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># verify_install.py</span></span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> ultralytics</span><br><span class="line"></span><br><span class="line"><span class="comment"># 確認 GPU 可用</span></span><br><span class="line">print(torch.cuda.is_available())       <span class="comment"># True</span></span><br><span class="line">print(torch.cuda.get_device_name(<span class="number">0</span>))   <span class="comment"># 顯卡名稱</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 確認 ultralytics 環境</span></span><br><span class="line">ultralytics.checks()</span><br></pre></td></tr></table></figure><p>正常輸出範例（GPU）：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">True</span><br><span class="line">NVIDIA GeForce RTX XXXX</span><br><span class="line">Ultralytics YOLOv8.x.x 🚀 Python-3.10.x torch-2.x.x CUDA:0 (NVIDIA GeForce RTX XXXX, XXXXMB)</span><br><span class="line">Setup complete ✅ (8 CPUs, 16.0 GB RAM, 100.0 GB disk)</span><br></pre></td></tr></table></figure><blockquote><p>💡 若 <code>torch.cuda.is_available()</code> 回傳 <code>False</code>，代表 PyTorch 的 GPU 版本未正確安裝，請重新確認 CUDA 版本並參照 pytorch.org 安裝對應版本。</p></blockquote><h2 id="💻-使用預訓練模型快速測試"><a href="#💻-使用預訓練模型快速測試" class="headerlink" title="💻 使用預訓練模型快速測試"></a>💻 使用預訓練模型快速測試</h2><p>安裝完成後，我們可以用 Ultralytics 提供的 CLI（指令列）方式快速測試 YOLOv8 是否正常運作。<br>YOLOv8 的 CLI 非常方便，適合快速驗證環境與模型效果。</p><blockquote><p>💡 以下範例使用的 <code>yolov8n.pt</code> 是以 <a href="https://cocodataset.org/" target="_blank" rel="external nofollow noopener noreferrer">COCO</a> 資料集預訓練的權重，能偵測 80 種常見物件（人、車、動物、家電、食物、家具等）。若目標物件不在這 80 類內，則需要自行蒐集資料訓練自訂模型，這正是後續幾篇要介紹的重點。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p></blockquote><h3 id="🔹-使用指令列（CLI）快速測試"><a href="#🔹-使用指令列（CLI）快速測試" class="headerlink" title="🔹 使用指令列（CLI）快速測試"></a>🔹 使用指令列（CLI）快速測試</h3><h4 id="🔸-偵測單張圖片"><a href="#🔸-偵測單張圖片" class="headerlink" title="🔸 偵測單張圖片"></a>🔸 偵測單張圖片</h4><p><img loading="lazy" src="/images/python/opencv/yolov8-install/01.png" alt="Python - 圖 2 (01)"></p><ul><li>⌨ 指令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 偵測圖片（使用 Ultralytics 官方測試圖片，第一次執行會自動下載模型權重）</span></span><br><span class="line">yolo detect predict model=yolov8n.pt <span class="built_in">source</span>=<span class="string">"https://ultralytics.com/images/bus.jpg"</span></span><br></pre></td></tr></table></figure></li><li>🖥️ 輸出：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Ultralytics 8.4.26  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">YOLOv8n summary (fused): 72 layers, 3,151,904 parameters, 0 gradients, 8.7 GFLOPs</span><br><span class="line"></span><br><span class="line">Found https://ultralytics.com/images/bus.jpg locally at bus.jpg</span><br><span class="line">image 1/1 D:\PythonWorkspace\opencv-tutorial\bus.jpg: 640x480 4 persons, 1 bus, 1 stop sign, 27.1ms</span><br><span class="line">Speed: 1.9ms preprocess, 27.1ms inference, 12.6ms postprocess per image at shape (1, 3, 640, 480)</span><br><span class="line">Results saved to D:\PythonWorkspace\opencv-tutorial\runs\detect\predict</span><br><span class="line"> Learn more at https://docs.ultralytics.com/modes/predict</span><br></pre></td></tr></table></figure><blockquote><p>📝 說明：這是測試環境是否正常的最簡單方式。模型會自動偵測圖片中的物件，並在終端機顯示偵測到的類別與信心度，同時會把結果圖片儲存到 runs/detect/predict 資料夾。</p></blockquote></li></ul><h4 id="🔸-偵測本地圖片"><a href="#🔸-偵測本地圖片" class="headerlink" title="🔸 偵測本地圖片"></a>🔸 偵測本地圖片</h4><p><img loading="lazy" src="/images/python/opencv/yolov8-install/02.png" alt="Python - 圖 3 (02)">​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p><ul><li>⌨ 指令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 偵測本地圖片（street.jpg 為「OpenCV 與深度學習框架整合」篇使用的 Pexels 街景素材）</span></span><br><span class="line">yolo detect predict model=yolov8n.pt <span class="built_in">source</span>=assets/street.jpg</span><br></pre></td></tr></table></figure></li><li>🖥️ 輸出：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Ultralytics 8.4.26  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">YOLOv8n summary (fused): 72 layers, 3,151,904 parameters, 0 gradients, 8.7 GFLOPs</span><br><span class="line"></span><br><span class="line">image 1/1 D:\PythonWorkspace\opencv-tutorial\assets\street.jpg: 448x640 1 person, 7 cars, 2 buss, 28.7ms</span><br><span class="line">Speed: 2.2ms preprocess, 28.7ms inference, 12.0ms postprocess per image at shape (1, 3, 448, 640)</span><br><span class="line">Results saved to D:\PythonWorkspace\opencv-tutorial\runs\detect\predict</span><br><span class="line"> Learn more at https://docs.ultralytics.com/modes/predict</span><br></pre></td></tr></table></figure><blockquote><p>📝 說明：把 <code>source</code> 改成你本機的圖片路徑即可。結果圖片會自動儲存，並在終端機顯示偵測到的物件數量與類別。</p></blockquote></li></ul><h4 id="🔸-偵測影片"><a href="#🔸-偵測影片" class="headerlink" title="🔸 偵測影片"></a>🔸 偵測影片</h4><p><img loading="lazy" src="/images/python/opencv/yolov8-install/03.gif" alt="Python - 圖 4 (03)"></p><ul><li>⌨ 指令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 偵測影片（street.mp4 同上）</span></span><br><span class="line">yolo detect predict model=yolov8n.pt <span class="built_in">source</span>=assets/street.mp4</span><br></pre></td></tr></table></figure></li><li>🖥️ 輸出：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Ultralytics 8.4.26  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">YOLOv8n summary (fused): 72 layers, 3,151,904 parameters, 0 gradients, 8.7 GFLOPs</span><br><span class="line"></span><br><span class="line">video 1/1 (frame 1/93) D:\PythonWorkspace\opencv-tutorial\assets\street.mp4: 384x640 8 persons, 2 cars, 3 traffic lights, 1 handbag, 20.4ms</span><br><span class="line">video 1/1 (frame 2/93) D:\PythonWorkspace\opencv-tutorial\assets\street.mp4: 384x640 8 persons, 2 cars, 3 traffic lights, 1 handbag, 7.6ms</span><br><span class="line">... 省略 ...</span><br><span class="line">video 1/1 (frame 91/93) D:\PythonWorkspace\opencv-tutorial\assets\street.mp4: 384x640 15 persons, 1 car, 3 traffic lights, 7.6ms</span><br><span class="line">video 1/1 (frame 92/93) D:\PythonWorkspace\opencv-tutorial\assets\street.mp4: 384x640 12 persons, 1 car, 2 traffic lights, 7.4ms</span><br><span class="line">video 1/1 (frame 93/93) D:\PythonWorkspace\opencv-tutorial\assets\street.mp4: 384x640 12 persons, 1 car, 2 traffic lights, 7.4ms</span><br><span class="line">Speed: 1.3ms preprocess, 7.8ms inference, 1.6ms postprocess per image at shape (1, 3, 384, 640)</span><br><span class="line">Results saved to D:\PythonWorkspace\opencv-tutorial\runs\detect\predict</span><br><span class="line"> Learn more at https://docs.ultralytics.com/modes/predict</span><br></pre></td></tr></table></figure><blockquote><p>📝 說明：YOLOv8 會逐幀處理影片，並在終端機顯示每幀的偵測結果。處理完後會自動把帶有偵測框的影片儲存起來。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p></blockquote></li></ul><h4 id="🔸-即時攝影機偵測"><a href="#🔸-即時攝影機偵測" class="headerlink" title="🔸 即時攝影機偵測"></a>🔸 即時攝影機偵測</h4><ul><li>⌨ 指令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 即時攝影機偵測（需有攝影機，0 代表預設裝置）</span></span><br><span class="line">yolo detect predict model=yolov8n.pt <span class="built_in">source</span>=0</span><br></pre></td></tr></table></figure></li><li>🖥️ 輸出：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Ultralytics 8.4.26  Python-3.10.11 torch-2.11.0+cu130 CUDA:0 (NVIDIA GeForce RTX 2060 SUPER, 8192MiB)</span><br><span class="line">YOLOv8n summary (fused): 72 layers, 3,151,904 parameters, 0 gradients, 8.7 GFLOPs</span><br><span class="line"></span><br><span class="line">[ WARN:0@0.001] global cap_ffmpeg_impl.hpp:1217 open VIDEOIO/FFMPEG: Failed list devices <span class="keyword">for</span> backend dshow</span><br><span class="line">[ERROR:0@2.857] global obsensor_uvc_stream_channel.cpp:163 cv::obsensor::getStreamChannelGroup Camera index out of range</span><br><span class="line">... 省略 ...</span><br><span class="line">ConnectionError: 1/1: 0... Failed to open 0</span><br></pre></td></tr></table></figure><blockquote><p>📝 說明：如果出現類似錯誤，通常是因為沒有連接攝影機，或攝影機被其他程式占用。解決方式：確認攝影機已連接，並關閉其他可能使用攝影機的程式（例如 Zoom、Teams、Camera App）。成功時會開啟一個視窗，即時顯示偵測結果。</p></blockquote></li></ul><h3 id="🔹-Python-API-方式"><a href="#🔹-Python-API-方式" class="headerlink" title="🔹 Python API 方式"></a>🔹 Python API 方式</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># inference.py</span></span><br><span class="line"><span class="keyword">from</span> ultralytics <span class="keyword">import</span> YOLO</span><br><span class="line"></span><br><span class="line"><span class="comment"># 載入預訓練模型（自動下載 yolov8n.pt）</span></span><br><span class="line">model = YOLO(<span class="string">"yolov8n.pt"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 對圖片推論（使用 Ultralytics 官方測試圖片）</span></span><br><span class="line">results = model(<span class="string">"https://ultralytics.com/images/bus.jpg"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 顯示結果</span></span><br><span class="line">results[<span class="number">0</span>].show()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看偵測到的物件</span></span><br><span class="line"><span class="keyword">for</span> box <span class="keyword">in</span> results[<span class="number">0</span>].boxes:</span><br><span class="line">    cls_id = int(box.cls)</span><br><span class="line">    conf   = float(box.conf)</span><br><span class="line">    label  = model.names[cls_id]</span><br><span class="line">    print(<span class="string">f"偵測到：<span class="subst">&#123;label&#125;</span>，信心度：<span class="subst">&#123;conf:<span class="number">.2</span>f&#125;</span>"</span>)</span><br></pre></td></tr></table></figure><p><img loading="lazy" src="/images/python/opencv/yolov8-install/04.png" alt="使用 YOLOv8 Python API 載入預訓練模型對圖片推論並輸出偵測到的物件類別與信心度"><br><em>圖：使用 YOLOv8 Python API 載入預訓練模型對圖片推論並輸出偵測到的物件類別與信心度</em></p><h2 id="⚠️-注意事項"><a href="#⚠️-注意事項" class="headerlink" title="⚠️ 注意事項"></a>⚠️ 注意事項</h2><ul><li><strong>第一次執行會自動下載模型權重</strong>：<code>yolov8n.pt</code> 約 6MB，<code>yolov8x.pt</code> 約 130MB，需要網路連線。</li><li><strong>ultralytics 會將下載的模型快取在 <code>~/.cache/ultralytics/</code></strong>（或 Windows 的 <code>%USERPROFILE%\.cache\ultralytics\</code>）。</li><li><strong>PyTorch 版本需與 CUDA 版本對應</strong>：安裝時請至 <a href="https://pytorch.org/get-started/locally/" target="_blank" rel="external nofollow noopener noreferrer">pytorch.org</a> 確認當前 CUDA 版本對應的 <code>--index-url</code>，版本不符會導致安裝到 CPU 版本。</li></ul><h2 id="📊-應用場景"><a href="#📊-應用場景" class="headerlink" title="📊 應用場景"></a>📊 應用場景</h2><ul><li><strong>快速驗證可行性</strong>：用預訓練模型先測試偵測效果，確認方向後再進入自訓練流程。</li><li><strong>直接部署通用偵測</strong>：若目標物件恰好在 COCO 80 類別內，可直接使用預訓練模型而無需訓練。</li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>YOLOv8 的環境設定相當簡單，<code>pip install ultralytics</code> 就能擁有完整的工具鏈。<br>下一步是 <a href="/python-opencv-20260407-python-opencv-yolov8-dataset"><strong>YOLOv8 資料集準備</strong></a>，學習如何組織自己的資料，讓 YOLOv8 能夠讀取並訓練。​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​​‌‌​‌‌‌​‌​​​‌‌​​​​‌​‌‌​‌‌​​​‌‌​‌‌​​</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br></p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件</a><br><a href="https://github.com/ultralytics/ultralytics" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics GitHub</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在 &lt;a href=&quot;/python-opencv-20260406-python-opencv-yolov8-in
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
  </entry>
  
  <entry>
    <title>Python | OpenCV 從分類到偵測 — YOLOv8 系列介紹</title>
    <link href="https://morosedog.gitlab.io/python-opencv-20260406-python-opencv-yolov8-intro/"/>
    <id>https://morosedog.gitlab.io/python-opencv-20260406-python-opencv-yolov8-intro/</id>
    <published>2026-04-06T01:00:00.000Z</published>
    <updated>2026-06-13T10:41:52.556Z</updated>
    
    <content type="html"><![CDATA[<h2 id="📚-前言"><a href="#📚-前言" class="headerlink" title="📚 前言"></a>📚 前言</h2><p>在上一篇 <a href="/python-opencv-20260405-python-opencv-inference-with-opencv"><strong>與 OpenCV 整合推論</strong></a> 結束後，我們完整走過了一個圖片分類模型從訓練到部署的全流程：</p><ul><li><a href="/python-opencv-20260302-python-opencv-framework">與深度學習框架整合</a></li><li><a href="/python-opencv-20260303-python-opencv-framework-practice">與深度學習框架整合 — 實作範例</a></li><li><a href="/python-opencv-20260323-python-opencv-model-training">模型訓練與微調</a><ul><li><a href="/python-opencv-20260324-python-opencv-data-collection">資料蒐集</a></li><li><a href="/python-opencv-20260324-python-opencv-video-dataset">影片資料集建立與品質檢查</a></li><li><a href="/python-opencv-20260325-python-opencv-data-annotation">資料標註</a></li><li><a href="/python-opencv-20260325-python-opencv-labelimg">LabelImg 標註工具實戰</a></li><li><a href="/python-opencv-20260325-python-opencv-dataset-split">資料集整理與驗證</a></li><li><a href="/python-opencv-20260326-python-opencv-model-selection">模型選擇與訓練</a><ul><li><a href="/python-opencv-20260327-python-opencv-transfer-learning">遷移學習與微調原理</a></li><li><a href="/python-opencv-20260328-python-opencv-pytorch-finetune">PyTorch 微調範例</a></li><li><a href="/python-opencv-20260329-python-opencv-tensorflow-finetune">TensorFlow/Keras 微調範例</a></li><li><a href="/python-opencv-20260330-python-opencv-data-augmentation">資料增強 (Data Augmentation)</a></li><li><a href="/python-opencv-20260331-python-opencv-overfitting">避免過擬合</a></li><li><a href="/python-opencv-20260401-python-opencv-gpu-acceleration">GPU 加速與效能優化</a></li></ul></li><li><a href="/python-opencv-20260402-python-opencv-model-inference">模型使用與推論</a><ul><li><a href="/python-opencv-20260403-python-opencv-model-evaluation">模型評估與測試</a></li><li><a href="/python-opencv-20260404-python-opencv-model-save-load">模型保存與載入</a></li><li><a href="/python-opencv-20260405-python-opencv-inference-with-opencv">與 OpenCV 整合</a></li></ul></li></ul></li></ul><p>這些章節以「<strong>圖片分類</strong>」為核心，從原理一路走到實際部署。<br>但如果你的目標是「<strong>在圖片中框出物件的位置並標出類別</strong>」，也就是物件偵測，那就是另一回事了。</p><h2 id="🔎-圖片分類-vs-物件偵測"><a href="#🔎-圖片分類-vs-物件偵測" class="headerlink" title="🔎 圖片分類 vs 物件偵測"></a>🔎 圖片分類 vs 物件偵測</h2><p><img loading="lazy" src="/images/python/opencv/yolov8-intro/classification_vs_detection.svg" alt="圖片分類 vs 物件偵測 ─ 兩者核心差異比較（輸出、問題、標註成本、模型複雜度）"><br><em>圖：圖片分類 vs 物件偵測 ─ 兩者核心差異比較（輸出、問題、標註成本、模型複雜度）</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌</p><p>前面章節用 ResNet 做分類，可以判斷「這張圖是貓還是狗」。<br>但如果想知道「這張圖裡有幾隻貓、分別在哪個位置」，ResNet 就不夠用了，需要物件偵測模型。</p><h2 id="💡-為什麼-YOLOv8-值得獨立一個系列"><a href="#💡-為什麼-YOLOv8-值得獨立一個系列" class="headerlink" title="💡 為什麼 YOLOv8 值得獨立一個系列"></a>💡 為什麼 YOLOv8 值得獨立一個系列</h2><p>YOLO（You Only Look Once）是目前最主流的即時物件偵測框架，而 YOLOv8 是由 Ultralytics 維護的第八代版本。</p><p>把它拉出來獨立成系列，原因有幾個：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌</p><p><strong>訓練方式截然不同</strong><br>前面章節的分類訓練需要手刻資料集 class、模型結構、訓練迴圈。<br>YOLOv8 只需準備一個 YAML 設定檔描述資料集路徑與類別，一行指令就能開始訓練：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yolo detect train data=my_dataset.yaml model=yolov8n.pt epochs=50</span><br></pre></td></tr></table></figure><p>這種極度簡化的 API 背後，是 Ultralytics 整合好的訓練、驗證、早停、日誌、模型儲存等全套機制。</p><p><img loading="lazy" src="/images/python/opencv/yolov8-intro/yolov8_advantages.svg" alt="YOLOv8 的兩大核心優勢 ─ 任務支援範圍廣 + 推論與匯出極度方便"><br><em>圖：YOLOv8 的兩大核心優勢 ─ 任務支援範圍廣 + 推論與匯出極度方便</em>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌</p><p><strong>前面學的知識仍然有用</strong><br>前幾章說明的 PyTorch 基礎、遷移學習原理、資料增強、模型評估方式，在 YOLO 的底層都是一樣的概念。<br>YOLO 系列是站在那些基礎上，學習如何用更高層的工具更快完成物件偵測任務。</p><h2 id="📋-YOLO-系列文章架構"><a href="#📋-YOLO-系列文章架構" class="headerlink" title="📋 YOLO 系列文章架構"></a>📋 YOLO 系列文章架構</h2><p><img loading="lazy" src="/images/python/opencv/yolov8-intro/yolov8_series_roadmap.svg" alt="YOLOv8 系列文章架構 ─ 建議按順序閱讀的完整學習路徑"><br><em>圖：YOLOv8 系列文章架構 ─ 建議按順序閱讀的完整學習路徑</em></p><p>接下來的系列會依這個順序展開，建議按順序閱讀：​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌</p><h3 id="🔹-YOLOv8-介紹與環境安裝"><a href="#🔹-YOLOv8-介紹與環境安裝" class="headerlink" title="🔹 YOLOv8 介紹與環境安裝"></a>🔹 YOLOv8 介紹與環境安裝</h3><ul><li><strong>你將學到</strong>：YOLOv8 的五種模型規格（n/s/m/l/x）怎麼選、安裝 Ultralytics、驗證環境、用預訓練模型跑第一次推論。</li><li>這是整個 YOLO 系列的起點，跑通這篇才能接著做資料準備與訓練。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260406-python-opencv-yolov8-install">YOLOv8 介紹與環境安裝</a></li></ul><h3 id="🔹-YOLOv8-資料集準備"><a href="#🔹-YOLOv8-資料集準備" class="headerlink" title="🔹 YOLOv8 資料集準備"></a>🔹 YOLOv8 資料集準備</h3><ul><li><strong>你將學到</strong>：YOLO 格式的標註檔結構（<code>.txt</code>）、資料集目錄配置、撰寫 <code>dataset.yaml</code>、切分 train/val/test。</li><li>YOLO 的資料格式與前面章節的分類格式完全不同，這篇說明如何正確組織。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260407-python-opencv-yolov8-dataset">YOLOv8 資料集準備</a></li></ul><h3 id="🔹-YOLOv8-預標籤（Pre-Label）"><a href="#🔹-YOLOv8-預標籤（Pre-Label）" class="headerlink" title="🔹 YOLOv8 預標籤（Pre-Label）"></a>🔹 YOLOv8 預標籤（Pre-Label）</h3><ul><li><strong>你將學到</strong>：用 COCO 預訓練模型對圖片自動框出初始標註，再用標註工具做人工修正，大幅降低標註工時。</li><li>完全手動標一張圖需要數十秒，預標籤可以讓這個成本降到只需「確認與微調」。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260408-python-opencv-yolov8-pre-label">YOLOv8 預標籤（Pre-Label）</a></li></ul><h3 id="🔹-YOLOv8-模型訓練"><a href="#🔹-YOLOv8-模型訓練" class="headerlink" title="🔹 YOLOv8 模型訓練"></a>🔹 YOLOv8 模型訓練</h3><ul><li><strong>你將學到</strong>：<code>yolo train</code> 指令與常用參數（<code>epochs</code>、<code>imgsz</code>、<code>batch</code>）、斷點續訓、用 Python API 訓練、訓練過程中的輸出目錄結構。</li><li>一行指令就能啟動訓練，但參數設定對結果影響很大，這篇說明各參數的意義。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260409-python-opencv-yolov8-training">YOLOv8 模型訓練</a></li></ul><h3 id="🔹-YOLOv8-訓練進階設定"><a href="#🔹-YOLOv8-訓練進階設定" class="headerlink" title="🔹 YOLOv8 訓練進階設定"></a>🔹 YOLOv8 訓練進階設定</h3><ul><li><strong>你將學到</strong>：內建資料增強參數的調整時機（Mosaic、fliplr、HSV）、防止過擬合的設定（Early Stopping、weight_decay、dropout），以及整合所有設定的完整訓練範例。</li><li>這篇是訓練篇的延伸，幫你把「能跑」進化到「訓練得好」。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260409-python-opencv-yolov8-training-advanced">YOLOv8 訓練進階設定</a></li></ul><h3 id="🔹-YOLOv8-訓練結果分析"><a href="#🔹-YOLOv8-訓練結果分析" class="headerlink" title="🔹 YOLOv8 訓練結果分析"></a>🔹 YOLOv8 訓練結果分析</h3><ul><li><strong>你將學到</strong>：讀懂 mAP、Precision、Recall、F1 曲線，解讀混淆矩陣，判斷是否需要補資料或調整參數。</li><li>訓練完不等於結束，這篇幫你判斷「模型夠不夠好」和「下一步該怎麼做」。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260410-python-opencv-yolov8-results">YOLOv8 訓練結果分析</a></li></ul><h3 id="🔹-YOLOv8-推論與匯出"><a href="#🔹-YOLOv8-推論與匯出" class="headerlink" title="🔹 YOLOv8 推論與匯出"></a>🔹 YOLOv8 推論與匯出</h3><ul><li><strong>你將學到</strong>：用訓練好的模型對圖片、影片、攝影機進行推論，匯出成 ONNX 接入 OpenCV，或匯出成 TensorRT 部署到 GPU 伺服器。</li><li>這是整個 YOLO 系列的最後一站，從訓練結果到實際可用的部署格式。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260411-python-opencv-yolov8-inference">YOLOv8 推論與匯出</a></li></ul><h3 id="🔹-YOLOv8-常見問題-Q-amp-A"><a href="#🔹-YOLOv8-常見問題-Q-amp-A" class="headerlink" title="🔹 YOLOv8 常見問題 Q&amp;A"></a>🔹 YOLOv8 常見問題 Q&amp;A</h3><ul><li><strong>你將學到</strong>：新增類別的兩種策略、遷移學習 Fine-tuning 做法、資料量與比例建議、訓練中斷續訓、模型選擇、效果診斷，以及用現有模型做預標籤、公開模型資源下載。</li><li>整理學完整個 YOLO 流程後最常出現的問題，以 Q&amp;A 形式逐一解答，適合遇到問題時快速查閱。</li><li>📖 詳細說明請參考：<a href="/python-opencv-20260411-python-opencv-yolov8-faq">YOLOv8 常見問題 Q&amp;A</a></li></ul><h2 id="🎯-結語"><a href="#🎯-結語" class="headerlink" title="🎯 結語"></a>🎯 結語</h2><p>如果前面的分類系列是「學會如何訓練模型的完整思路」，YOLO 系列就是「把這個思路用最有效率的方式實作在物件偵測任務上」。</p><p>下一篇是 <a href="/python-opencv-20260406-python-opencv-yolov8-install"><strong>YOLOv8 介紹與環境安裝</strong></a>，從 Ultralytics 套件的安裝開始，跑通第一次推論。</p><p>📖 如在學習過程中遇到疑問，或是想了解更多相關主題，建議回顧一下 <a href="/python-opencv-20260106-python-opencv-index"><strong>Python | OpenCV 系列導讀</strong></a>，掌握完整的章節目錄，方便快速找到你需要的內容。<br><br>​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​​‌‌​​‌​​​‌‌​​​​​​‌‌​​‌​​​‌‌​‌‌​​​‌‌​​​​​​‌‌​‌​​​​‌‌​​​​​​‌‌​‌‌​​​‌​‌‌​‌​‌‌‌​​​​​‌‌‌‌​​‌​‌‌‌​‌​​​‌‌​‌​​​​‌‌​‌‌‌‌​‌‌​‌‌‌​​​‌​‌‌​‌​‌‌​‌‌‌‌​‌‌‌​​​​​‌‌​​‌​‌​‌‌​‌‌‌​​‌‌​​​‌‌​‌‌‌​‌‌​​​‌​‌‌​‌​‌‌‌‌​​‌​‌‌​‌‌‌‌​‌‌​‌‌​​​‌‌​‌‌‌‌​‌‌‌​‌‌​​​‌‌‌​​​​​‌​‌‌​‌​‌‌​‌​​‌​‌‌​‌‌‌​​‌‌‌​‌​​​‌‌‌​​‌​​‌‌​‌‌‌‌</p><blockquote><p>註：以上參考了<br><a href="https://docs.ultralytics.com/" target="_blank" rel="external nofollow noopener noreferrer">Ultralytics YOLOv8 官方文件</a><br><a href="https://arxiv.org/abs/1506.02640" target="_blank" rel="external nofollow noopener noreferrer">YOLO 論文 — You Only Look Once</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;📚-前言&quot;&gt;&lt;a href=&quot;#📚-前言&quot; class=&quot;headerlink&quot; title=&quot;📚 前言&quot;&gt;&lt;/a&gt;📚 前言&lt;/h2&gt;&lt;p&gt;在上一篇 &lt;a href=&quot;/python-opencv-20260405-python-opencv-infere
      
    
    </summary>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/categories/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/"/>
    
      <category term="07.物件偵測與辨識篇" scheme="https://morosedog.gitlab.io/categories/Python/OpenCV/07-%E7%89%A9%E4%BB%B6%E5%81%B5%E6%B8%AC%E8%88%87%E8%BE%A8%E8%AD%98%E7%AF%87/"/>
    
    
      <category term="Python" scheme="https://morosedog.gitlab.io/tags/Python/"/>
    
      <category term="OpenCV" scheme="https://morosedog.gitlab.io/tags/OpenCV/"/>
    
      <category term="YOLO" scheme="https://morosedog.gitlab.io/tags/YOLO/"/>
    
  </entry>
  
</feed>
