<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://devblogs.damingsoft.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://devblogs.damingsoft.com/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-09-15T05:34:38+00:00</updated><id>https://devblogs.damingsoft.com/feed.xml</id><title type="html">大名维森软件开发者博客</title><subtitle>大名维森软件的开发者博客。大名维森软件的主要产品是文档扫描和图像处理SDK。
</subtitle><author><name>大名维森软件</name></author><entry><title type="html">微信小程序集成 Dynamsoft Barcode Reader：实现1D/2D多码扫描</title><link href="https://devblogs.damingsoft.com/wechat-miniapp-barcode-scan/" rel="alternate" type="text/html" title="微信小程序集成 Dynamsoft Barcode Reader：实现1D/2D多码扫描" /><published>2025-12-24T16:25:53+00:00</published><updated>2025-12-24T16:25:53+00:00</updated><id>https://devblogs.damingsoft.com/wechat-miniapp-barcode-scan</id><content type="html" xml:base="https://devblogs.damingsoft.com/wechat-miniapp-barcode-scan/"><![CDATA[<p>在移动应用开发中，条形码和二维码扫描是一个常见的需求。很多应用都自带了扫码功能，但是只能做单码扫描，一般用于个人登录，付款等简单场景。在物流，仓储等商业场景中，扫码需求则更加复杂，需要支持多码识别，高精度识别以及多种码制的支持。</p>

<p>本文将分享如何在微信小程序中集成 Dynamsoft Barcode Reader JS WebAssembly SDK，实现高性能的1D/2D多码扫描功能。</p>

<p><img src="/album/2025/12/wechat-miniapp-dev-tool.png" alt="小程序开发工具" /></p>

<h2 id="一技术选型为什么选择-dynamsoft-barcode-reader">一、技术选型：为什么选择 Dynamsoft Barcode Reader</h2>

<h3 id="11-需求背景">1.1 需求背景</h3>

<p>微信小程序虽然提供了原生的 <code class="language-plaintext highlighter-rouge">wx.scanCode</code> API，但在某些场景下存在明显的局限性：</p>
<ul>
  <li>无法自定义扫码界面和交互逻辑</li>
  <li>识别精度和速度受限于系统实现</li>
  <li>无法满足复杂的扫码需求（如批量扫码、特定格式识别等）</li>
</ul>

<p>因此，我们需要一个能够在小程序环境中运行的高性能扫码引擎。</p>

<h3 id="12-dynamsoft-barcode-reader-的优势">1.2 Dynamsoft Barcode Reader 的优势</h3>

<p>Dynamsoft Barcode Reader 是一款企业级的条码识别 SDK，其 WebAssembly 版本特别适合在微信小程序中使用：</p>
<ul>
  <li><strong>高精度识别</strong>：支持多种条码格式，识别率高</li>
  <li><strong>高性能</strong>：基于 WebAssembly，性能接近原生</li>
  <li><strong>丰富的配置</strong>：提供多种识别模板和参数调优选项</li>
  <li><strong>跨平台</strong>：同一套代码可在不同平台运行</li>
</ul>

<p>SDK包含：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BarcodeReader.js 
dbr.worker.js
dynamsoft-barcode-reader-bundle-ml-simd.js
dynamsoft-barcode-reader-bundle-ml-simd.wasm.br
</code></pre></div></div>

<h2 id="二初步集成从-wasm-加载开始">二、初步集成：从 WASM 加载开始</h2>

<h3 id="21-微信小程序的-wasm-支持">2.1 微信小程序的 WASM 支持</h3>

<p>微信小程序提供了 <code class="language-plaintext highlighter-rouge">WXWebAssembly.instantiate</code> API 来加载 WASM 模块。与标准的 Web 环境不同，小程序有一些特殊的限制：</p>
<ul>
  <li>不支持直接使用 <code class="language-plaintext highlighter-rouge">WebAssembly</code> 对象，必须使用 <code class="language-plaintext highlighter-rouge">WXWebAssembly</code></li>
  <li>WASM 文件必须放在小程序包内，不能动态下载</li>
  <li>主包大小限制为 2MB，总包大小限制为 20MB</li>
</ul>

<h3 id="22-wasm-文件的加载">2.2 WASM 文件的加载</h3>

<p>在 Dynamsoft Barcode Reader 中，我们需要正确配置 WASM 文件的路径：</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">await</span> <span class="nx">DBR</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">(</span><span class="dl">"</span><span class="s2">workers/dbr.worker.js</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">subpackages/dbr/wasm/dynamsoft-barcode-reader-bundle-ml-simd.wasm.br</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="三分包加载解决主包大小限制">三、分包加载：解决主包大小限制</h2>

<p>Dynamsoft Barcode Reader 的 WASM 文件压缩（<code class="language-plaintext highlighter-rouge">.wasm.br</code>）后小于2MB。将 WASM 文件放入分包目录 <code class="language-plaintext highlighter-rouge">subpackages/dbr/</code> 中，这样就不会占用主包的空间。</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"subpackages"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"root"</span><span class="p">:</span><span class="w"> </span><span class="s2">"subpackages/dbr"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dbr"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"pages"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"pages/scan/index"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"pages/camera-scan/index"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"pages/file-scan/index"</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="31-延迟初始化策略">3.1 延迟初始化策略</h3>

<p>由于 WASM 文件位于分包中，如果主包启动时（<code class="language-plaintext highlighter-rouge">app.js</code> 的 <code class="language-plaintext highlighter-rouge">onLaunch</code>）立即尝试加载 WASM，可能会因为分包尚未下载完成而失败。</p>

<p>最佳实践是采用<strong>延迟初始化</strong>：</p>

<ol>
  <li>在 <code class="language-plaintext highlighter-rouge">app.js</code> 中定义 <code class="language-plaintext highlighter-rouge">initSDK</code> 方法，但不立即调用。</li>
  <li>在分包页面的 <code class="language-plaintext highlighter-rouge">onLoad</code> 生命周期中调用 <code class="language-plaintext highlighter-rouge">app.initSDK()</code>。</li>
</ol>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.js</span>
<span class="nx">initSDK</span><span class="p">:</span> <span class="k">async</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">globalData</span><span class="p">.</span><span class="nx">wasmInitialized</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
  <span class="c1">// 加载 WASM</span>
  <span class="k">await</span> <span class="nx">DBR</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">(</span><span class="dl">"</span><span class="s2">workers/dbr.worker.js</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">subpackages/dbr/wasm/dynamsoft-barcode-reader-bundle-ml-simd.wasm.br</span><span class="dl">"</span><span class="p">);</span>
  <span class="c1">// 初始化 License</span>
  <span class="k">await</span> <span class="nx">DBR</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">.</span><span class="nx">initLicense</span><span class="p">(</span><span class="dl">"</span><span class="s2">YOUR_LICENSE</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">globalData</span><span class="p">.</span><span class="nx">wasmInitialized</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// subpackages/dbr/pages/scan/index.js</span>
<span class="nl">onLoad</span><span class="p">:</span> <span class="k">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">await</span> <span class="nx">getApp</span><span class="p">().</span><span class="nx">initSDK</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="四视频帧数据传输和处理">四、视频帧数据传输和处理</h2>
<p>通过<code class="language-plaintext highlighter-rouge">onCameraFrame</code>接口获取视频帧数据，然后发送到worker中进行处理。针对<strong>iOS</strong>, <strong>Android</strong>和<strong>HarmonyOS</strong>，需要使用不同的方法。<code class="language-plaintext highlighter-rouge">capture</code>接口封装了worker以及帧数据处理逻辑。</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">listener</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">onCameraFrame</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nx">frame</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">isDetecting</span> <span class="o">||</span> <span class="k">this</span><span class="p">.</span><span class="nx">isProcessing</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

      <span class="k">this</span><span class="p">.</span><span class="nx">isProcessing</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
      <span class="k">try</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">getApp</span><span class="p">();</span>
          <span class="k">if</span> <span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">globalData</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">)</span> <span class="p">{</span>
              <span class="k">this</span><span class="p">.</span><span class="nx">currentFrameSize</span> <span class="o">=</span> <span class="p">{</span> <span class="na">width</span><span class="p">:</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="na">height</span><span class="p">:</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">height</span> <span class="p">};</span>
              
              <span class="kd">let</span> <span class="nx">captureOptions</span> <span class="o">=</span> <span class="p">{</span>
                  <span class="na">width</span><span class="p">:</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
                  <span class="na">height</span><span class="p">:</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
                  <span class="na">stride</span><span class="p">:</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">width</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span>
                  <span class="na">format</span><span class="p">:</span> <span class="mi">10</span>
              <span class="p">};</span>

              <span class="kd">let</span> <span class="nx">transfer</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
              <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">isAndroid</span><span class="p">)</span> <span class="p">{</span>
                  <span class="nx">captureOptions</span><span class="p">.</span><span class="nx">bytes</span> <span class="o">=</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
                  <span class="nx">transfer</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
              <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">isHarmony</span><span class="p">)</span> <span class="p">{</span>
                  <span class="nx">captureOptions</span><span class="p">.</span><span class="nx">bytes</span> <span class="o">=</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
                  <span class="nx">transfer</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
              <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">isIOS</span><span class="p">)</span> <span class="p">{</span>
                  <span class="nx">captureOptions</span><span class="p">.</span><span class="nx">bytes</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
              <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                  <span class="nx">captureOptions</span><span class="p">.</span><span class="nx">bytes</span> <span class="o">=</span> <span class="nx">frame</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
              <span class="p">}</span>

              <span class="k">try</span> <span class="p">{</span>
                  <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">app</span><span class="p">.</span><span class="nx">globalData</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">.</span><span class="nx">capture</span><span class="p">(</span><span class="nx">captureOptions</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">dynamsoftTemplate</span><span class="p">,</span> <span class="nx">transfer</span><span class="p">);</span>
                  <span class="k">this</span><span class="p">.</span><span class="nx">handleDynamsoftResult</span><span class="p">({</span> <span class="na">captureResult</span><span class="p">:</span> <span class="nx">result</span> <span class="p">});</span>
              <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
                  <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Capture failed:</span><span class="dl">"</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span>
                  <span class="k">this</span><span class="p">.</span><span class="nx">isProcessing</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
              <span class="p">}</span>
          <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
              <span class="k">this</span><span class="p">.</span><span class="nx">isProcessing</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
          <span class="p">}</span>
      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">ex</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">ex</span><span class="p">);</span>
          <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">isDetecting</span><span class="p">)</span> <span class="p">{</span>
              <span class="k">this</span><span class="p">.</span><span class="nx">setData</span><span class="p">({</span> <span class="na">scanResult</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Error: </span><span class="dl">"</span> <span class="o">+</span> <span class="p">(</span><span class="nx">ex</span><span class="p">.</span><span class="nx">message</span> <span class="o">||</span> <span class="nx">ex</span><span class="p">)</span> <span class="p">});</span>
          <span class="p">}</span>
          <span class="k">this</span><span class="p">.</span><span class="nx">isProcessing</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
      <span class="p">}</span>
  <span class="p">});</span>
  <span class="kd">const</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">getApp</span><span class="p">();</span>

  <span class="nx">listener</span><span class="p">.</span><span class="nx">start</span><span class="p">({</span>
      <span class="na">worker</span><span class="p">:</span> <span class="nx">app</span><span class="p">.</span><span class="nx">globalData</span><span class="p">.</span><span class="nx">BarcodeReader</span><span class="p">.</span><span class="nx">worker</span>
  <span class="p">});</span>
</code></pre></div></div>

<ul>
  <li>iOS上可以通过<a href="https://developers.weixin.qq.com/miniprogram/dev/api/worker/Worker.getCameraFrameData.html">getCameraFrameData</a>在worker中获取帧数据。这样就不需要通过前端获取帧数据，提升性能。</li>
  <li>Android上可以通过<code class="language-plaintext highlighter-rouge">postMessage(buf, [buf])</code>传输ArrayBuffer，避免数据复制，实现零拷贝。</li>
  <li>HarmonyOS上支持比较差，只能通过<code class="language-plaintext highlighter-rouge">postMessage(buf)</code>拷贝数据的方式传输。</li>
</ul>

<h2 id="五源码获取">五、源码获取</h2>
<p>需要源码，可以联系cnsupport@damingsoft.com，或者微信hzdamingsoft。</p>

<p><img src="/album/2025/12/contact.png" alt="联系人" /></p>

<h2 id="六小程序演示">六、小程序演示</h2>

<p><img src="/album/2025/12/wechat-miniapp-barcode-scan.jpg" alt="小程序" /></p>

<p><img src="/album/2025/12/wechat-miniapp-barcode-scanner.jpg" alt="演示页面" /></p>]]></content><author><name>大名维森软件</name></author><category term="barcode" /><category term="barcode" /><summary type="html"><![CDATA[本文介绍了在微信小程序中集成Dynamsoft Barcode Reader JS WebAssembly SDK实现高性能条码扫描的方法。针对小程序环境特点，文章详细讲解了WASM文件加载、分包策略和延迟初始化等关键技术点。特别分析了iOS、Android和HarmonyOS平台下视频帧数据传输处理的差异方案，包括worker线程优化和零拷贝技术。通过该方案，开发者可以在小程序中实现企业级的多码扫描功能，满足物流仓储等复杂场景需求，同时兼顾性能和包体积限制。最终效果展示表明该方案具有优异的识别精度和响应速]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">如何在Windows上使用SANE扫描文档</title><link href="https://devblogs.damingsoft.com/windows-sane-scanner-access-via-wsl/" rel="alternate" type="text/html" title="如何在Windows上使用SANE扫描文档" /><published>2025-11-14T03:25:53+00:00</published><updated>2025-11-14T03:25:53+00:00</updated><id>https://devblogs.damingsoft.com/windows-sane-scanner-access-via-wsl</id><content type="html" xml:base="https://devblogs.damingsoft.com/windows-sane-scanner-access-via-wsl/"><![CDATA[<p>SANE是用于连接文档扫描仪的一套API，主要用于UNIX系统。与TWAIN（另一种主要用于Windows的文档扫描API）相比，它有几个优点：</p>

<ol>
  <li>TWAIN不会将用户界面与设备的驱动程序分开。这样很难通过网络访问图像捕获设备。而SANE则可以轻松地在纯命令行环境中使用。</li>
  <li>SANE内置了对各种扫描仪的支持，而使用TWAIN必须下载并安装单独的驱动程序。</li>
</ol>

<p>有一些在Windows上运行SANE的尝试。但大多数做法仍然需要Linux环境。借助Windows的Linux子系统（WSL），这一过程变得更加容易。</p>

<p>继续阅读以了解如何做到这一点。</p>

<h2 id="将usb设备连接到wsl">将USB设备连接到WSL</h2>

<ol>
  <li>
    <p>安装Linux WSL发行版后，将其版本设置为2以使用WSL 2。</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:\Users\admin&gt; wsl -l  -v
  NAME            STATE           VERSION
  Debian          Running         1
PS C:\Users\admin&gt; wsl --set-version Debian 2
</code></pre></div>    </div>
  </li>
  <li>在Windows上安装USBIPD。可以在<a href="https://github.com/dorssel/usbipd-win/releases">GitHub</a>上找到它的安装程序。</li>
  <li>
    <p>使用USBIPD将USB设备连接到WSL。</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>PS C:\Users\admin&gt; usbipd list # list USB devices connected to the host
PS C:\Users\admin&gt; usbipd bind --busid &lt;busid&gt; # share the device. You can find the bus id in the previous step
PS C:\Users\admin&gt; usbipd attach --wsl --busid &lt;busid&gt; # attach the device to WSL
</code></pre></div>    </div>
  </li>
  <li>
    <p>在Linux中运行<code class="language-plaintext highlighter-rouge">lsusb</code>，可以在列表中找到USB设备。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 003: ID 04c5:132e Fujitsu, Ltd <span class="k">fi</span><span class="nt">-7160</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="使用sane扫描文档">使用SANE扫描文档</h2>

<ol>
  <li>
    <p>安装SANE。这里，我们的系统是Debian。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>apt <span class="nb">install </span>sane-utils
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用<code class="language-plaintext highlighter-rouge">scanimage</code>命令行工具列出扫描仪和扫描文档。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scanimage <span class="nt">-L</span>
device <span class="s1">'fujitsu:fi-7160:151477'</span> is a FUJITSU <span class="k">fi</span><span class="nt">-7160</span> scanner
<span class="nv">$ </span>scanimage <span class="nt">-o</span> scanned.png <span class="c"># save the document to an image</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="在浏览器中扫描文档">在浏览器中扫描文档</h2>

<p>接下来，我们将使用Dynamic Web TWAIN SDK创建一个网页，在Windows上的浏览器中扫描文档，并使用WSL中运行的SANE后端。</p>

<ol>
  <li>
    <p>在WSL上，安装Dynamic Web TWAIN服务。可以在它的<a href="https://app.unpkg.com/dwt@19.2.0/files/dist/dist">npm包</a>上找到安装程序。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dpkg <span class="nt">-i</span> DynamicWebTWAINServiceSetup.deb
</code></pre></div>    </div>

    <p>该服务将作为HTTP服务器在网页和扫描仪之间进行通信。您可以通过访问<a href="http://127.0.0.1:18625">http://127.0.0.1:18625</a>来检查它是否已安装。</p>

    <p>每次系统启动时，还需要启动两个进程。以下是启动它们的命令：</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nohup "/opt/dynamsoft/Dynamic Web TWAIN Service 19/DynamsoftScanning" gtkproxy &amp;
nohup "/opt/dynamsoft/Dynamic Web TWAIN Service 19/DynamsoftScanningMgr" &amp;
</code></pre></div>    </div>

    <p>可以自己创建一个服务来启动它们。</p>
  </li>
  <li>
    <p>使用以下代码编写一个网页来扫描文档并另存为PDF：</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;title&gt;</span>Scan via SANE on Windows<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/dwt@latest/dist/dynamsoft.webtwain.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"AcquireImage();"</span><span class="nt">&gt;</span>Scan<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"SaveAsPDF();"</span><span class="nt">&gt;</span>Save as PDF<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"dwtcontrolContainer"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">Host</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">local.dynamsoft.com</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">ResourcesPath</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://cdn.jsdelivr.net/npm/dwt@latest/dist</span><span class="dl">"</span><span class="p">;</span>
    <span class="c1">//You need to set the service installer location here since the installer's size exceeds jsdelivr's limit.</span>
    <span class="c1">//You'd better host the installers in your own environment.</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">ServiceInstallerLocation</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">https://unpkg.com/dwt/dist/dist/</span><span class="dl">'</span><span class="p">;</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">ProductKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">LICENSE-KEY</span><span class="dl">'</span><span class="p">;</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">Containers</span> <span class="o">=</span> <span class="p">[{</span> <span class="na">ContainerId</span><span class="p">:</span> <span class="dl">'</span><span class="s1">dwtcontrolContainer</span><span class="dl">'</span><span class="p">,</span> <span class="na">Width</span><span class="p">:</span> <span class="mi">270</span><span class="p">,</span> <span class="na">Height</span><span class="p">:</span> <span class="mi">350</span> <span class="p">}];</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
      <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">Load</span><span class="p">();</span>
    <span class="p">};</span>
    <span class="kd">var</span> <span class="nx">DWTObject</span><span class="p">;</span>
    <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">RegisterEvent</span><span class="p">(</span><span class="dl">"</span><span class="s2">OnWebTwainReady</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
      <span class="c1">// dwtcontrolContainer is the id of the DIV to create the WebTwain instance in.</span>
      <span class="nx">DWTObject</span> <span class="o">=</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">GetWebTwain</span><span class="p">(</span><span class="dl">'</span><span class="s1">dwtcontrolContainer</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">});</span>
    <span class="kd">function</span> <span class="nx">AcquireImage</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">DWTObject</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">DWTObject</span><span class="p">.</span><span class="nx">SelectSourceAsync</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
          <span class="k">return</span> <span class="nx">DWTObject</span><span class="p">.</span><span class="nx">AcquireImageAsync</span><span class="p">({</span>
            <span class="na">PixelType</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">EnumDWT_PixelType</span><span class="p">.</span><span class="nx">TWPT_RGB</span><span class="p">,</span>
            <span class="na">Resolution</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
            <span class="na">IfCloseSourceAfterAcquire</span><span class="p">:</span> <span class="kc">true</span>
          <span class="p">});</span>
        <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">exp</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">alert</span><span class="p">(</span><span class="nx">exp</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
        <span class="p">});</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">SaveAsPDF</span><span class="p">(){</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">DWTObject</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">DWTObject</span><span class="p">.</span><span class="nx">ConvertToBlob</span><span class="p">(</span>
          <span class="nx">DWTObject</span><span class="p">.</span><span class="nx">SelectAllImages</span><span class="p">(),</span>
          <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DWT</span><span class="p">.</span><span class="nx">EnumDWT_ImageType</span><span class="p">.</span><span class="nx">IT_PDF</span><span class="p">,</span>
          <span class="kd">function</span> <span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="nx">indices</span><span class="p">,</span> <span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">size</span><span class="p">);</span>
            <span class="nx">DownloadBlobAsFile</span><span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="dl">"</span><span class="s2">scanned_document.pdf</span><span class="dl">"</span><span class="p">);</span>
          <span class="p">},</span>
          <span class="kd">function</span> <span class="p">(</span><span class="nx">errorCode</span><span class="p">,</span> <span class="nx">errorString</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">errorString</span><span class="p">);</span>
          <span class="p">},</span>
        <span class="p">);</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">DownloadBlobAsFile</span><span class="p">(</span><span class="nx">blob</span><span class="p">,</span> <span class="nx">fileName</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">var</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">link</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>
      <span class="nx">link</span><span class="p">.</span><span class="nx">download</span> <span class="o">=</span> <span class="nx">fileName</span><span class="p">;</span>
      <span class="nx">link</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
    <span class="p">}</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>现在，我们可以使用SANE在Windows上扫描文档了。</p>

<p><img src="/album/2025/11/sane-demo-page.jpg" alt="演示页面" /></p>

<h2 id="源代码">源代码</h2>

<p><a href="https://github.com/tony-xlh/Dynamic-Web-TWAIN-samples/tree/main/Windows-SANE">https://github.com/tony-xlh/Dynamic-Web-TWAIN-samples/tree/main/Windows-SANE</a></p>]]></content><author><name>大名维森软件</name></author><category term="文档扫描" /><summary type="html"><![CDATA[本文分享了如何连接USB设备到Windows的WSL并用SANE扫描文档。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">如何在网页中嵌入PDF</title><link href="https://devblogs.damingsoft.com/how-to-embed-pdf-in-html/" rel="alternate" type="text/html" title="如何在网页中嵌入PDF" /><published>2025-09-03T05:25:53+00:00</published><updated>2025-09-03T05:25:53+00:00</updated><id>https://devblogs.damingsoft.com/how-to-embed-pdf-in-html</id><content type="html" xml:base="https://devblogs.damingsoft.com/how-to-embed-pdf-in-html/"><![CDATA[<p>在网页中直接嵌入PDF文件是个有用的操作，用户可以直接在浏览器中查看和编辑PDF文件。在本文中，我们将讨论不同的嵌入方法。</p>

<h2 id="使用html元素嵌入pdf文件">使用HTML元素嵌入PDF文件</h2>

<p>我们可以使用HTML的内置元素通过指定PDF的URL来嵌入PDF文件。</p>

<ol>
  <li>
    <p>使用<code class="language-plaintext highlighter-rouge">iframe</code>。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/iframe.html">在线演示</a>）</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;iframe</span> <span class="na">src=</span><span class="s">"ddv-sample.pdf"</span> <span class="na">width=</span><span class="s">"100%"</span> <span class="na">height=</span><span class="s">"600px"</span><span class="nt">&gt;&lt;/iframe&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用<code class="language-plaintext highlighter-rouge">embed</code>。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/embed.html">在线演示</a>）</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;embed</span> <span class="na">src=</span><span class="s">"ddv-sample.pdf"</span> <span class="na">type=</span><span class="s">"application/pdf"</span> <span class="na">width=</span><span class="s">"100%"</span> <span class="na">height=</span><span class="s">"600px"</span> <span class="nt">/&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用<code class="language-plaintext highlighter-rouge">object</code>。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/object.html">在线演示</a>）</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;object</span> <span class="na">data=</span><span class="s">"ddv-sample.pdf"</span> <span class="na">type=</span><span class="s">"application/pdf"</span> <span class="na">width=</span><span class="s">"100%"</span> <span class="na">height=</span><span class="s">"600px"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;p&gt;</span>Your browser does not support PDFs.
        <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"ddv-sample.pdf"</span><span class="nt">&gt;</span>Download the PDF<span class="nt">&lt;/a&gt;</span>.
    <span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/object&gt;</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>推荐使用<code class="language-plaintext highlighter-rouge">iframe</code>。它的兼容性最好。</p>

<table>
  <thead>
    <tr>
      <th>标签</th>
      <th>推荐</th>
      <th>支持后备</th>
      <th>问题</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;iframe&gt;</code></td>
      <td>推荐</td>
      <td>不支持</td>
      <td>默认情况会有边框</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;object&gt;</code></td>
      <td>更语义化</td>
      <td>支持</td>
      <td>可能不适用于旧版和移动浏览器</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&lt;embed&gt;</code></td>
      <td>不太推荐</td>
      <td>不支持</td>
      <td>可能不适用于旧版浏览器</td>
    </tr>
  </tbody>
</table>

<p>在桌面上，以上三个标签都会使用浏览器的内置PDF查看器打开PDF。在移动浏览器上，行为各不相同。在Android上，浏览器会提供下载链接。在iOS上，浏览器将呈现PDF的第一页。</p>

<h2 id="将pdf转换为图像或html以嵌入pdf文件">将PDF转换为图像或HTML以嵌入PDF文件</h2>

<p>我们可以使用Apache PDFBox等库预先将PDF文件转换为图像，或者使用pdf2htmlex转换PDF为HTML，以将其嵌入网页中。</p>

<p>转换成图像质量可能会下降，也可能增大体积，带来更高的网络开销。此外，文本也无法进行搜索或复制。</p>

<p>转换为HTML效果更好。它可以使查看PDF就像查看普通网页一样。但无法进行PDF编辑和注释等操作。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/ddv-sample.html">转换后的示例</a>）</p>

<h2 id="使用pdf-javascript库嵌入pdf文件">使用PDF JavaScript库嵌入PDF文件</h2>

<p>我们可以使用第三方PDF库来嵌入PDF文件。它有以下好处：</p>

<ol>
  <li>我们可以为不同的浏览器和平台提供统一的体验。</li>
  <li>我们可以定制用户界面。</li>
  <li>我们可以使用更多功能，如PDF注释和PDF编辑。</li>
  <li>出于安全考虑，我们可以阻止下载PDF文件。</li>
</ol>

<p>当然，由于渲染发生在客户端，因此对客户端有更高的要求。</p>

<p>有几个库可供使用：</p>

<ol>
  <li>PDF.js。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/pdfjs.html">在线演示</a>）</li>
  <li>PDFium.js。（<a href="https://tony-xlh.github.io/Embed-PDF-in-HTML/pdfium.html">在线演示</a>）</li>
</ol>

<p><a href="https://github.com/mozilla/pdf.js/">PDF.js</a>和<a href="https://github.com/Jaewoook/pdfium.js/">PDFium.js</a>提供了PDF渲染功能，但它们没有一个像样的查看器。</p>

<p><a href="https://www.dynamsoft.com/document-viewer/overview/">Dynamsoft Document Viewer</a>使用PDFium作为引擎，并提供功能齐全的UI来查看和编辑PDF。更推荐使用它来嵌入PDF。</p>

<p><a href="https://demo.dynamsoft.com/document-viewer/">Dynamsoft Document Viewer的在线演示</a></p>

<h2 id="总结">总结</h2>

<p>以下是对这三种方法的总结。</p>

<table>
  <thead>
    <tr>
      <th>方法</th>
      <th>技术</th>
      <th>优点</th>
      <th>缺点</th>
      <th>适用于</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>原生HTML元素</td>
      <td>iframe、object、embed</td>
      <td>•易于实现<br />•利用浏览器的内置查看器</td>
      <td>•用户体验不一致（尤其是在移动设备上）<br />•对UI和功能的控制有限<br />•无法阻止下载</td>
      <td>快速集成基本的PDF查看功能的情况</td>
    </tr>
    <tr>
      <td>转换为图像或HTML</td>
      <td>PDFBox、pdf2htmlEX</td>
      <td>•统一的查看体验<br />•转换为HTML时感觉就像原生网页</td>
      <td>•功能缺失（编辑）<br />•可能生成较大的文件（图像）或复杂的HTML</td>
      <td>不需要怎么编辑的档案文件。</td>
    </tr>
    <tr>
      <td>JavaScript库</td>
      <td>PDF.js、PDFium.js、<br />Dynamsoft Document Viewer</td>
      <td>•跨平台最统一的体验<br />•完整的功能集（注释、编辑等）<br />•可定制的UI<br />•安全控制（例如禁用下载）</td>
      <td>•更高的客户端要求（JavaScript、处理能力）<br />•更复杂的实现<br />•较大的初始页面加载量</td>
      <td>专业应用程序，需要一个可靠、功能丰富、可定制的PDF查看器，并为所有用户提供一致的界面。</td>
    </tr>
  </tbody>
</table>

<h2 id="源代码">源代码</h2>

<p>欢迎下载源代码并尝试使用：</p>

<p><a href="https://github.com/tony-xlh/Embed-PDF-in-HTML">https://github.com/tony-xlh/Embed-PDF-in-HTML</a></p>]]></content><author><name>大名维森软件</name></author><category term="文档扫描" /><summary type="html"><![CDATA[本文分享了如何在网页中嵌入PDF。讨论了所有可能的方法。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">命令行调用扫描仪扫描文档</title><link href="https://devblogs.damingsoft.com/scan-documents-from-command-line/" rel="alternate" type="text/html" title="命令行调用扫描仪扫描文档" /><published>2025-07-31T05:10:53+00:00</published><updated>2025-07-31T05:10:53+00:00</updated><id>https://devblogs.damingsoft.com/scan-documents-from-command-line</id><content type="html" xml:base="https://devblogs.damingsoft.com/scan-documents-from-command-line/"><![CDATA[<p>在本文中，我们将讨论如何从命令行（CLI）扫描文档，以便自动化文档扫描工作或者集成扫描功能到其它应用中。</p>

<p>有不同的API可以访问文档扫描仪，下面是一张对比表格。</p>

<table>
  <thead>
    <tr>
      <th>特点</th>
      <th>TWAIN</th>
      <th>WIA (Windows Image Acquisition)</th>
      <th>SANE (Scanner Access Now Easy)</th>
      <th>ICA (Image Capture Architecture)</th>
      <th>eSCL</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>开发者</td>
      <td>TWAIN Working Group</td>
      <td>Microsoft</td>
      <td>SANE Open-Source Community</td>
      <td>Apple</td>
      <td>Mopria</td>
    </tr>
    <tr>
      <td>操作系统</td>
      <td>Windows、macOS、Linux（部分）</td>
      <td>Windows</td>
      <td>Linux、macOS、类Unix</td>
      <td>macOS、iOS</td>
      <td>跨平台（Windows/macOS/Linux/mobile）</td>
    </tr>
    <tr>
      <td>支持的扫描仪</td>
      <td>广泛</td>
      <td>较广泛</td>
      <td>较广泛（社区支持）</td>
      <td>较广泛</td>
      <td>仅网络扫描仪/MFP</td>
    </tr>
    <tr>
      <td>功能</td>
      <td>高级控制（ADF、条形码检测等）</td>
      <td>颜色模式等基本控制</td>
      <td>中级控制</td>
      <td>颜色模式等基本控制</td>
      <td>颜色模式等基本控制</td>
    </tr>
  </tbody>
</table>

<p>我们将使用这些API从命令行扫描文档。由于只有SANE提供了命令行工具，而其他没有，因此我们需要编写命令行工具来使用其他API。</p>

<h2 id="sane">SANE</h2>

<p>SANE有一个命令行工具<code class="language-plaintext highlighter-rouge">scanimage</code>。以下是它的基本用法：</p>

<ol>
  <li>
    <p>列出连接的扫描仪。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scanimage <span class="nt">-L</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用指定的扫描仪获取图像。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scanimage <span class="nt">-d</span> <span class="s2">"scanner name"</span> <span class="nt">-o</span> out.png
</code></pre></div>    </div>
  </li>
</ol>

<p>我们将要编写的其他API的命令行工具将具有相同的用法。</p>

<h2 id="twain">TWAIN</h2>

<p>TWAIN接口是用C++实现的，并有一个Python库。我们将使用Python编写命令行工具。</p>

<p>以下是关键部分的代码：</p>

<ol>
  <li>
    <p>导入库。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">twain</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>列出扫描仪。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="n">twain</span><span class="p">.</span><span class="n">SourceManager</span><span class="p">()</span> <span class="k">as</span> <span class="n">sm</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">source</span> <span class="ow">in</span> <span class="n">sm</span><span class="p">.</span><span class="n">source_list</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="n">source</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>用扫描仪扫描。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">BytesIO</span>
<span class="k">with</span> <span class="n">twain</span><span class="p">.</span><span class="n">SourceManager</span><span class="p">()</span> <span class="k">as</span> <span class="n">sm</span><span class="p">:</span>
   <span class="n">src</span> <span class="o">=</span> <span class="n">sm</span><span class="p">.</span><span class="n">open_source</span><span class="p">(</span><span class="s">"scanner_name"</span><span class="p">)</span>
   <span class="n">src</span><span class="p">.</span><span class="n">request_acquire</span><span class="p">(</span><span class="n">show_ui</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">modal_ui</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
   <span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">remaining_count</span><span class="p">)</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">xfer_image_natively</span><span class="p">()</span>
   <span class="n">bmp_bytes</span> <span class="o">=</span> <span class="n">twain</span><span class="p">.</span><span class="n">dib_to_bm_file</span><span class="p">(</span><span class="n">handle</span><span class="p">)</span>
   <span class="n">img</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">BytesIO</span><span class="p">(</span><span class="n">bmp_bytes</span><span class="p">),</span> <span class="n">formats</span><span class="o">=</span><span class="p">[</span><span class="s">"bmp"</span><span class="p">])</span>
   <span class="n">img</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="s">"output_path"</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="wia">WIA</h2>

<p>WIA提供API和COM的调用方式。我们将使用Python和COM来使用WIA。</p>

<p>以下是关键部分的代码：</p>

<ol>
  <li>
    <p>导入库。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
<span class="kn">import</span> <span class="nn">pythoncom</span>
<span class="kn">from</span> <span class="nn">win32com.client</span> <span class="kn">import</span> <span class="n">Dispatch</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>列出扫描仪。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">manager</span> <span class="o">=</span> <span class="n">Dispatch</span><span class="p">(</span><span class="s">"WIA.DeviceManager"</span><span class="p">)</span>
<span class="n">devices</span> <span class="o">=</span> <span class="n">manager</span><span class="p">.</span><span class="n">DeviceInfos</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Available scanners:"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">devices</span><span class="p">.</span><span class="n">Count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
    <span class="n">device</span> <span class="o">=</span> <span class="n">devices</span><span class="p">.</span><span class="n">Item</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
    <span class="c1"># Check if the device is a scanner (Type = 1)
</span>    <span class="k">if</span> <span class="n">device</span><span class="p">.</span><span class="n">Type</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  Name: </span><span class="si">{</span><span class="n">device</span><span class="p">.</span><span class="n">Properties</span><span class="p">[</span><span class="s">'Name'</span><span class="p">].</span><span class="n">Value</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  ID: </span><span class="si">{</span><span class="n">device</span><span class="p">.</span><span class="n">DeviceID</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  Description: </span><span class="si">{</span><span class="n">device</span><span class="p">.</span><span class="n">Properties</span><span class="p">[</span><span class="s">'Description'</span><span class="p">].</span><span class="n">Value</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"  ----------------"</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>用扫描仪扫描。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">wia</span> <span class="o">=</span> <span class="n">Dispatch</span><span class="p">(</span><span class="s">"WIA.CommonDialog"</span><span class="p">)</span>
<span class="n">manager</span> <span class="o">=</span> <span class="n">Dispatch</span><span class="p">(</span><span class="s">"WIA.DeviceManager"</span><span class="p">)</span>

<span class="n">devices</span> <span class="o">=</span> <span class="n">manager</span><span class="p">.</span><span class="n">DeviceInfos</span>
<span class="n">selected_device</span> <span class="o">=</span> <span class="bp">None</span>
<span class="n">scanner_name</span> <span class="o">=</span> <span class="s">"target scanner name"</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">devices</span><span class="p">.</span><span class="n">Count</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
    <span class="n">device</span> <span class="o">=</span> <span class="n">devices</span><span class="p">.</span><span class="n">Item</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">device</span><span class="p">.</span><span class="n">Type</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">device</span><span class="p">.</span><span class="n">Properties</span><span class="p">[</span><span class="s">'Name'</span><span class="p">].</span><span class="n">Value</span> <span class="o">==</span> <span class="n">scanner_name</span><span class="p">:</span>
        <span class="n">selected_device</span> <span class="o">=</span> <span class="n">device</span><span class="p">.</span><span class="n">Connect</span><span class="p">()</span> <span class="c1"># Select the scanner by name
</span>        <span class="k">break</span>

<span class="n">img</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">if</span> <span class="n">selected_device</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">img</span> <span class="o">=</span> <span class="n">wia</span><span class="p">.</span><span class="n">ShowAcquireImage</span><span class="p">()</span>  <span class="c1"># Show scanning dialog with scanner selection
</span><span class="k">else</span><span class="p">:</span>
    <span class="n">img</span> <span class="o">=</span> <span class="n">wia</span><span class="p">.</span><span class="n">ShowTransfer</span><span class="p">(</span><span class="n">selected_device</span><span class="p">.</span><span class="n">Items</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>  <span class="c1"># Transfer the scanned image using the selected scanner
</span>
<span class="c1">#save the image
</span><span class="n">pil_img</span> <span class="o">=</span> <span class="n">Image</span><span class="p">.</span><span class="n">fromarray</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
<span class="n">pil_img</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">output_path</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="escl">eSCL</h2>

<p>eSCL是一个RESTful接口。网络扫描仪通过Bonjour进行广播，客户端可以找到它们并发送HTTP请求来扫描文档。我们也将使用Python编写扫描工具。</p>

<ol>
  <li>
    <p>导入库。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">zeroconf</span> <span class="kn">import</span> <span class="n">ServiceBrowser</span><span class="p">,</span> <span class="n">Zeroconf</span>
<span class="kn">from</span> <span class="nn">requests</span> <span class="kn">import</span> <span class="n">get</span> <span class="k">as</span> <span class="n">requests_get</span><span class="p">,</span> <span class="n">post</span> <span class="k">as</span> <span class="n">requests_post</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>通过检测类型为<code class="language-plaintext highlighter-rouge">_uscan_tcp.local.</code>的Bonjour服务来列出扫描仪。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">ESCLScannerListener</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">scanners</span> <span class="o">=</span> <span class="p">[]</span>

    <span class="k">def</span> <span class="nf">add_service</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">zeroconf</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="n">info</span> <span class="o">=</span> <span class="n">zeroconf</span><span class="p">.</span><span class="n">get_service_info</span><span class="p">(</span><span class="nb">type</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">info</span><span class="p">:</span>
            <span class="n">addresses</span> <span class="o">=</span> <span class="p">[</span><span class="s">"%s:%d"</span> <span class="o">%</span> <span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">port</span><span class="p">)</span> <span class="k">for</span> <span class="n">addr</span> <span class="ow">in</span> <span class="n">info</span><span class="p">.</span><span class="n">addresses</span><span class="p">]</span>
            <span class="n">scanner_info</span> <span class="o">=</span> <span class="p">{</span>
                <span class="s">'name'</span><span class="p">:</span> <span class="n">name</span><span class="p">,</span>
                <span class="s">'type'</span><span class="p">:</span> <span class="nb">type</span><span class="p">,</span>
                <span class="s">'addresses'</span><span class="p">:</span> <span class="n">info</span><span class="p">.</span><span class="n">addresses</span><span class="p">,</span>
                <span class="s">'port'</span><span class="p">:</span> <span class="n">info</span><span class="p">.</span><span class="n">port</span><span class="p">,</span>
                <span class="s">'properties'</span><span class="p">:</span> <span class="n">info</span><span class="p">.</span><span class="n">properties</span>
            <span class="p">}</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">scanners</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">scanner_info</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">remove_service</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">zeroconf</span><span class="p">,</span> <span class="nb">type</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Scanner removed: </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">discover_escl_scanners</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">2</span><span class="p">):</span>
    <span class="n">zeroconf</span> <span class="o">=</span> <span class="n">Zeroconf</span><span class="p">()</span>
    <span class="n">listener</span> <span class="o">=</span> <span class="n">ESCLScannerListener</span><span class="p">()</span>
    <span class="n">browser</span> <span class="o">=</span> <span class="n">ServiceBrowser</span><span class="p">(</span><span class="n">zeroconf</span><span class="p">,</span> <span class="s">"_uscan._tcp.local."</span><span class="p">,</span> <span class="n">listener</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Discovering ESCL scanners for </span><span class="si">{</span><span class="n">timeout</span><span class="si">}</span><span class="s"> seconds..."</span><span class="p">)</span>
    <span class="n">time</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span>
    <span class="n">zeroconf</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">listener</span><span class="p">.</span><span class="n">scanners</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>用扫描仪扫描。扫描配置以XML表示。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">scan</span><span class="p">(</span><span class="n">scanner_address</span><span class="p">,</span> <span class="n">output_path</span><span class="o">=</span><span class="s">"scanned.jpg"</span><span class="p">):</span>
    <span class="n">xml</span> <span class="o">=</span> <span class="s">'''&lt;scan:ScanSettings xmlns:scan="http://schemas.hp.com/imaging/escl/2011/05/03" xmlns:dd="http://www.hp.com/schemas/imaging/con/dictionaries/1.0/" xmlns:dd3="http://www.hp.com/schemas/imaging/con/dictionaries/2009/04/06" xmlns:fw="http://www.hp.com/schemas/imaging/con/firewall/2011/01/05" xmlns:scc="http://schemas.hp.com/imaging/escl/2011/05/03" xmlns:pwg="http://www.pwg.org/schemas/2010/12/sm"&gt;&lt;pwg:Version&gt;2.1&lt;/pwg:Version&gt;&lt;scan:Intent&gt;Photo&lt;/scan:Intent&gt;&lt;pwg:ScanRegions&gt;&lt;pwg:ScanRegion&gt;&lt;pwg:Height&gt;3300&lt;/pwg:Height&gt;&lt;pwg:Width&gt;2550&lt;/pwg:Width&gt;&lt;pwg:XOffset&gt;0&lt;/pwg:XOffset&gt;&lt;pwg:YOffset&gt;0&lt;/pwg:YOffset&gt;&lt;/pwg:ScanRegion&gt;&lt;/pwg:ScanRegions&gt;&lt;pwg:InputSource&gt;Platen&lt;/pwg:InputSource&gt;&lt;scan:DocumentFormatExt&gt;image/jpeg&lt;/scan:DocumentFormatExt&gt;&lt;scan:XResolution&gt;300&lt;/scan:XResolution&gt;&lt;scan:YResolution&gt;300&lt;/scan:YResolution&gt;&lt;scan:ColorMode&gt;Grayscale8&lt;/scan:ColorMode&gt;&lt;scan:CompressionFactor&gt;25&lt;/scan:CompressionFactor&gt;&lt;scan:Brightness&gt;1000&lt;/scan:Brightness&gt;&lt;scan:Contrast&gt;1000&lt;/scan:Contrast&gt;&lt;/scan:ScanSettings&gt;'''</span>

    <span class="n">resp</span> <span class="o">=</span> <span class="n">requests_post</span><span class="p">(</span><span class="s">'http://{0}/eSCL/ScanJobs'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">scanner_address</span><span class="p">),</span> <span class="n">data</span><span class="o">=</span><span class="n">xml</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s">'Content-Type'</span><span class="p">:</span> <span class="s">'text/xml'</span><span class="p">})</span>
    <span class="k">if</span> <span class="n">resp</span><span class="p">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">201</span><span class="p">:</span>
        <span class="n">url</span> <span class="o">=</span> <span class="s">'{0}/NextDocument'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">resp</span><span class="p">.</span><span class="n">headers</span><span class="p">[</span><span class="s">'Location'</span><span class="p">])</span>
        <span class="n">r</span> <span class="o">=</span> <span class="n">requests_get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span><span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="ica">ICA</h2>

<p>使用Image Capture API稍微复杂点，我们将创建一个Swift命令行项目来实现该工具。</p>

<p>以下是关键部分的代码：</p>

<ol>
  <li>
    <p>创建叫ScannerManager的类以列出扫描仪。</p>

    <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">ScannerManager</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">ICDeviceBrowserDelegate</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">deviceBrowser</span><span class="p">:</span> <span class="kt">ICDeviceBrowser</span><span class="o">!</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">scanners</span><span class="p">:</span> <span class="p">[</span><span class="kt">ICScannerDevice</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">currentScanner</span><span class="p">:</span> <span class="kt">ICScannerDevice</span><span class="p">?</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">scanCompletionHandler</span><span class="p">:</span> <span class="p">((</span><span class="kt">Result</span><span class="o">&lt;</span><span class="kt">URL</span><span class="p">,</span> <span class="kt">Error</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">targetURL</span><span class="p">:</span> <span class="kt">URL</span><span class="p">?</span>

    <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
        <span class="nf">setupDeviceBrowser</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">setupDeviceBrowser</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">deviceBrowser</span> <span class="o">=</span> <span class="kt">ICDeviceBrowser</span><span class="p">()</span>
        <span class="n">deviceBrowser</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
        <span class="k">let</span> <span class="nv">mask</span> <span class="o">=</span> <span class="kt">ICDeviceTypeMask</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span>
                    <span class="kt">ICDeviceTypeMask</span><span class="o">.</span><span class="n">scanner</span><span class="o">.</span><span class="n">rawValue</span> <span class="o">|</span>
                    <span class="kt">ICDeviceLocationTypeMask</span><span class="o">.</span><span class="n">local</span><span class="o">.</span><span class="n">rawValue</span> <span class="o">|</span>
                    <span class="kt">ICDeviceLocationTypeMask</span><span class="o">.</span><span class="n">bonjour</span><span class="o">.</span><span class="n">rawValue</span> <span class="o">|</span>
                    <span class="kt">ICDeviceLocationTypeMask</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span>
        <span class="n">deviceBrowser</span><span class="o">.</span><span class="n">browsedDeviceTypeMask</span> <span class="o">=</span> <span class="n">mask</span><span class="o">!</span>
        <span class="n">deviceBrowser</span><span class="o">.</span><span class="nf">start</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">listScanners</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">([</span><span class="kt">ICScannerDevice</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">asyncAfter</span><span class="p">(</span><span class="nv">deadline</span><span class="p">:</span> <span class="o">.</span><span class="nf">now</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="nf">completion</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">scanners</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// MARK: - ICDeviceBrowserDelegate</span>

    <span class="kd">func</span> <span class="nf">deviceBrowser</span><span class="p">(</span><span class="n">_</span> <span class="nv">browser</span><span class="p">:</span> <span class="kt">ICDeviceBrowser</span><span class="p">,</span> <span class="n">didAdd</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ICDevice</span><span class="p">,</span> <span class="nv">moreComing</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">scanner</span> <span class="o">=</span> <span class="n">device</span> <span class="k">as?</span> <span class="kt">ICScannerDevice</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="n">scanners</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">scanner</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">deviceBrowser</span><span class="p">(</span><span class="n">_</span> <span class="nv">browser</span><span class="p">:</span> <span class="kt">ICDeviceBrowser</span><span class="p">,</span> <span class="n">didRemove</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ICDevice</span><span class="p">,</span> <span class="nv">moreGoing</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">index</span> <span class="o">=</span> <span class="n">scanners</span><span class="o">.</span><span class="nf">firstIndex</span><span class="p">(</span><span class="nv">where</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">==</span> <span class="n">device</span> <span class="p">})</span> <span class="p">{</span>
            <span class="n">scanners</span><span class="o">.</span><span class="nf">remove</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">index</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>让这个类继承<code class="language-plaintext highlighter-rouge">ICScannerDeviceDelegate</code>并添加与扫描相关的函数。</p>

    <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">device</span><span class="p">(</span><span class="n">_</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ICDevice</span><span class="p">,</span> <span class="n">didCloseSessionWithError</span> <span class="nv">error</span><span class="p">:</span> <span class="p">(</span><span class="n">any</span> <span class="kt">Error</span><span class="p">)?)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"did close"</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">didRemove</span><span class="p">(</span><span class="n">_</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ICDevice</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"did remove"</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">device</span><span class="p">(</span><span class="n">_</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ICDevice</span><span class="p">,</span> <span class="n">didOpenSessionWithError</span> <span class="nv">error</span><span class="p">:</span> <span class="p">(</span><span class="n">any</span> <span class="kt">Error</span><span class="p">)?)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"did open"</span><span class="p">)</span>
    <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="nf">asyncAfter</span><span class="p">(</span><span class="nv">deadline</span><span class="p">:</span> <span class="o">.</span><span class="nf">now</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="o">=</span> <span class="k">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="k">guard</span> <span class="k">let</span> <span class="nv">scanner</span> <span class="o">=</span> <span class="n">currentScanner</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
        <span class="n">scanner</span><span class="o">.</span><span class="n">transferMode</span> <span class="o">=</span> <span class="o">.</span><span class="n">fileBased</span>
        <span class="n">scanner</span><span class="o">.</span><span class="n">downloadsDirectory</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">fileURLWithPath</span><span class="p">:</span> <span class="kt">NSTemporaryDirectory</span><span class="p">())</span>
        <span class="n">scanner</span><span class="o">.</span><span class="n">documentName</span> <span class="o">=</span> <span class="s">"scan"</span>
        <span class="n">scanner</span><span class="o">.</span><span class="n">documentUTI</span> <span class="o">=</span> <span class="n">kUTTypeJPEG</span> <span class="k">as</span> <span class="kt">String</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">functionalUnit</span> <span class="o">=</span> <span class="n">scanner</span><span class="o">.</span><span class="n">selectedFunctionalUnit</span> <span class="k">as?</span> <span class="kt">ICScannerFunctionalUnit</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">resolutionIndex</span> <span class="o">=</span> <span class="n">functionalUnit</span><span class="o">.</span><span class="n">supportedResolutions</span><span class="o">.</span><span class="nf">integerGreaterThanOrEqualTo</span><span class="p">(</span><span class="mi">300</span><span class="p">)</span> <span class="p">??</span> <span class="n">functionalUnit</span><span class="o">.</span><span class="n">supportedResolutions</span><span class="o">.</span><span class="n">last</span>
            <span class="k">if</span> <span class="k">let</span> <span class="nv">resolutionIndex</span> <span class="o">=</span> <span class="n">resolutionIndex</span> <span class="p">??</span> <span class="n">functionalUnit</span><span class="o">.</span><span class="n">supportedResolutions</span><span class="o">.</span><span class="n">last</span> <span class="p">{</span>
                <span class="n">functionalUnit</span><span class="o">.</span><span class="n">resolution</span> <span class="o">=</span> <span class="n">resolutionIndex</span>
            <span class="p">}</span>

            <span class="k">let</span> <span class="nv">a4Width</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mf">210.0</span> <span class="c1">// mm</span>
            <span class="k">let</span> <span class="nv">a4Height</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mf">297.0</span> <span class="c1">// mm</span>
            <span class="k">let</span> <span class="nv">widthInPoints</span> <span class="o">=</span> <span class="n">a4Width</span> <span class="o">*</span> <span class="mf">72.0</span> <span class="o">/</span> <span class="mf">25.4</span> <span class="c1">// convert to point</span>
            <span class="k">let</span> <span class="nv">heightInPoints</span> <span class="o">=</span> <span class="n">a4Height</span> <span class="o">*</span> <span class="mf">72.0</span> <span class="o">/</span> <span class="mf">25.4</span>

            <span class="n">functionalUnit</span><span class="o">.</span><span class="n">scanArea</span> <span class="o">=</span> <span class="kt">NSMakeRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">widthInPoints</span><span class="p">,</span> <span class="n">heightInPoints</span><span class="p">)</span>
            <span class="n">functionalUnit</span><span class="o">.</span><span class="n">pixelDataType</span> <span class="o">=</span> <span class="o">.</span><span class="kt">RGB</span>
            <span class="n">functionalUnit</span><span class="o">.</span><span class="n">bitDepth</span> <span class="o">=</span> <span class="o">.</span><span class="n">depth8Bits</span>

            <span class="n">scanner</span><span class="o">.</span><span class="nf">requestScan</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: - ICScannerDeviceDelegate</span>

<span class="kd">func</span> <span class="nf">scannerDevice</span><span class="p">(</span><span class="n">_</span> <span class="nv">scanner</span><span class="p">:</span> <span class="kt">ICScannerDevice</span><span class="p">,</span> <span class="n">didScanTo</span> <span class="nv">url</span><span class="p">:</span> <span class="kt">URL</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">print</span><span class="p">(</span><span class="s">"did scan to"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">url</span><span class="o">.</span><span class="n">absoluteString</span><span class="p">)</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">targetURL</span> <span class="o">=</span> <span class="n">targetURL</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nf">scanCompletionHandler</span><span class="p">?(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="kt">NSError</span><span class="p">(</span><span class="nv">domain</span><span class="p">:</span> <span class="s">"ScannerError"</span><span class="p">,</span> <span class="nv">code</span><span class="p">:</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="nv">userInfo</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSLocalizedDescriptionKey</span><span class="p">:</span> <span class="s">"No target URL set"</span><span class="p">])))</span>
        <span class="k">return</span>
    <span class="p">}</span>
    <span class="k">do</span> <span class="p">{</span>
        <span class="k">try</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">moveItem</span><span class="p">(</span><span class="nv">at</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="n">targetURL</span><span class="p">)</span>
        <span class="nf">scanCompletionHandler</span><span class="p">?(</span><span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="n">targetURL</span><span class="p">))</span>
    <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
        <span class="nf">scanCompletionHandler</span><span class="p">?(</span><span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="n">error</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// MARK: - Scan Operations</span>

<span class="kd">func</span> <span class="nf">startScan</span><span class="p">(</span><span class="nv">scanner</span><span class="p">:</span> <span class="kt">ICScannerDevice</span><span class="p">,</span> <span class="nv">outputPath</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Result</span><span class="o">&lt;</span><span class="kt">URL</span><span class="p">,</span> <span class="kt">Error</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">currentScanner</span> <span class="o">=</span> <span class="n">scanner</span>
    <span class="n">scanCompletionHandler</span> <span class="o">=</span> <span class="n">completion</span>
    <span class="n">targetURL</span> <span class="o">=</span> <span class="kt">URL</span><span class="p">(</span><span class="nv">fileURLWithPath</span><span class="p">:</span> <span class="n">outputPath</span><span class="p">)</span>

    <span class="n">scanner</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
    <span class="n">scanner</span><span class="o">.</span><span class="nf">requestOpenSession</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="dynamic-web-twain-restful-api">Dynamic Web TWAIN RESTful API</h2>

<p><a href="https://www.dynamsoft.com/web-twain/overview">Dynamic Web TWAIN</a>提供RESTful API功能，支持使用TWAIN、WIA、SANE、ICA和eSCL等API扫描文档。您可以在<a href="https://www.dynamsoft.com/web-twain/restfulapi/">这个页面</a>上找到它的详细信息。</p>

<p>以下是使用Dynamic Web TWAIN的RESTful API的好处：</p>

<ol>
  <li>提供了调用主流的扫描API的统一接口，支持控制扫描仪的各种功能，支持不同平台。</li>
  <li>通过网络共享扫描仪，这样移动设备也可以访问文档扫描仪。</li>
  <li>支持各种编程语言去使用文档扫描功能。</li>
</ol>

<p>以下是使用<a href="https://pypi.org/project/twain-wia-sane-scanner/">Python版封装</a>的关键部分的代码：</p>

<ol>
  <li>
    <p>导入库并声明有关变量。可以在此处申请<a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dwt">许可证</a>。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">dynamsoftservice</span> <span class="kn">import</span> <span class="n">ScannerController</span><span class="p">,</span> <span class="n">ScannerType</span>
<span class="n">license_key</span> <span class="o">=</span> <span class="s">"LICENSE-KEY"</span>
<span class="n">host</span> <span class="o">=</span> <span class="s">"http://127.0.0.1:18622"</span>
<span class="n">scannerController</span> <span class="o">=</span> <span class="n">ScannerController</span><span class="p">()</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>列出扫描仪。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">list_scanners</span><span class="p">():</span>
    <span class="s">"""List all available scanners"""</span>
    <span class="n">scanners</span> <span class="o">=</span> <span class="n">scannerController</span><span class="p">.</span><span class="n">getDevices</span><span class="p">(</span><span class="n">host</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">scanners</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>用扫描仪扫描。</p>

    <div class="language-py highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">scan_document</span><span class="p">(</span><span class="n">output_path</span><span class="o">=</span><span class="s">"scan.png"</span><span class="p">,</span> <span class="n">scanner_name</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="s">"""
    Scan a document using Web TWAIN service and save as image file

    Parameters:
        output_path: Path to save scanned image
        scanner_name: Name of specific scanner to use (None shows dialog)
    """</span>
    <span class="n">scanners</span> <span class="o">=</span> <span class="n">list_scanners</span><span class="p">()</span>
    <span class="n">selectedScanner</span> <span class="o">=</span> <span class="bp">None</span>
    <span class="k">if</span> <span class="n">scanner_name</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">scanner</span> <span class="ow">in</span> <span class="n">scanners</span><span class="p">:</span>
            <span class="k">if</span> <span class="n">scanner</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span> <span class="o">==</span> <span class="n">scanner_name</span><span class="p">:</span>
                <span class="n">selectedScanner</span> <span class="o">=</span> <span class="n">scanner</span>
                <span class="k">break</span>

    <span class="n">parameters</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"license"</span><span class="p">:</span> <span class="n">license_key</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="n">selectedScanner</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">parameters</span><span class="p">[</span><span class="s">"device"</span><span class="p">]</span> <span class="o">=</span> <span class="n">selectedScanner</span><span class="p">[</span><span class="s">"device"</span><span class="p">]</span>

    <span class="n">parameters</span><span class="p">[</span><span class="s">"config"</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"IfShowUI"</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span>
        <span class="s">"PixelType"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
        <span class="s">"Resolution"</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
        <span class="s">"IfFeederEnabled"</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span>
        <span class="s">"IfDuplexEnabled"</span><span class="p">:</span> <span class="bp">False</span><span class="p">,</span>
    <span class="p">}</span>

    <span class="n">job</span> <span class="o">=</span> <span class="n">scannerController</span><span class="p">.</span><span class="n">createJob</span><span class="p">(</span><span class="n">host</span><span class="p">,</span> <span class="n">parameters</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="n">job</span><span class="p">)</span>
    <span class="k">if</span> <span class="s">"jobuid"</span> <span class="ow">in</span> <span class="n">job</span><span class="p">:</span>
        <span class="n">job_id</span> <span class="o">=</span> <span class="n">job</span><span class="p">[</span><span class="s">"jobuid"</span><span class="p">]</span>
        <span class="n">stream</span> <span class="o">=</span> <span class="n">scannerController</span><span class="p">.</span><span class="n">getImageStreams</span><span class="p">(</span><span class="n">host</span><span class="p">,</span><span class="n">job_id</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
        <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span><span class="s">"wb"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">stream</span><span class="p">)</span>
            <span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">output_path</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>除了RESTful API，Dynamic Web TWAIN还提供了一个JavaScript库，该库具有专用的文档查看器、完整的文档扫描API包装、本地缓存和各种补充API，以提供基于浏览器的文档扫描解决方案。<a href="https://demo.dynamsoft.com/web-twain/">访问其在线demo</a>进行试用。</p>

<h2 id="源代码">源代码</h2>

<p>可以在GitHub上获取源代码，了解命令行工具的具体使用说明：</p>

<p><a href="https://github.com/tony-xlh/document-scanner-cli/">https://github.com/tony-xlh/document-scanner-cli/</a></p>

<h2 id="外部链接">外部链接</h2>

<ul>
  <li><a href="https://twain.org/">TWAIN</a></li>
  <li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/desktop/wia/-wia-startpage">Windows Image Acquisition (WIA)</a></li>
  <li><a href="http://sane-project.org/">SANE</a></li>
  <li><a href="https://developer.apple.com/documentation/imagecapturecore">Image Capture</a></li>
  <li><a href="https://mopria.org/spec-download">Mopria eSCL</a></li>
</ul>]]></content><author><name>大名维森软件</name></author><category term="文档扫描" /><summary type="html"><![CDATA[本文分享了如何在Windows、macOS和Linux上使用TWAIN、WIA、ICA、SANE和eSCL等API从命令行调用扫描仪扫描文档。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">在手机浏览器上扫描文档并打印</title><link href="https://devblogs.damingsoft.com/scan-and-print-document-phone-javascript/" rel="alternate" type="text/html" title="在手机浏览器上扫描文档并打印" /><published>2025-03-18T06:29:53+00:00</published><updated>2025-03-18T06:29:53+00:00</updated><id>https://devblogs.damingsoft.com/scan-and-print-document-phone-javascript</id><content type="html" xml:base="https://devblogs.damingsoft.com/scan-and-print-document-phone-javascript/"><![CDATA[<style>
img{
  max-height:400px;
}
</style>

<p>使用手机扫描文档并打印方便快捷。在本文中，我们将编写一个支持在手机浏览器中扫描和打印文档的网页应用。</p>

<p>使用Dynamsoft的以下SDK：</p>

<ul>
  <li><a href="https://www.dynamsoft.com/document-viewer/docs/introduction/index.html">Dynamsoft Document Viewer</a>：它提供了一套用于文档捕获、编辑和保存为PDF的查看器。</li>
  <li><a href="https://www.dynamsoft.com/document-normalizer/overview/">Dynamsoft Document Normalizer</a>：提供文档边缘检测功能。</li>
</ul>

<p>这个Web应用可以捕获文档图像，调整图像大小，并进行漂白以供打印。</p>

<p>图像捕获：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/capture-viewer.jpg" alt="捕获查看器" /></p>

<p>扫描的图像（裁剪的）：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/scanned.jpg" alt="已扫描" /></p>

<p>用于打印的黑白图像：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/black-white.jpg" alt="黑白图" /></p>

<p><a href="https://tony-xlh.github.io/scan-and-print-document-javascript/">在线demo</a></p>

<h2 id="新建html文件">新建HTML文件</h2>

<p>创建一个新的HTML文件，内容如下。</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;title&gt;</span>Document Scanning via Camera<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;style&gt;</span>
    <span class="nf">#container</span> <span class="p">{</span>
      <span class="nl">max-width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
      <span class="nl">height</span><span class="p">:</span> <span class="m">480px</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;h2&gt;</span>Document Scanning via Camera<span class="nt">&lt;/h2&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Camera:
    <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"select-camera"</span><span class="nt">&gt;&lt;/select&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Resolution:
    <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"select-resolution"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"640x480"</span><span class="nt">&gt;</span>640x480<span class="nt">&lt;/option&gt;</span>
      <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"1280x720"</span><span class="nt">&gt;</span>1280x720<span class="nt">&lt;/option&gt;</span>
      <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"1920x1080"</span> <span class="na">selected</span><span class="nt">&gt;</span>1920x1080<span class="nt">&lt;/option&gt;</span>
      <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"3840x2160"</span><span class="nt">&gt;</span>3840x2160<span class="nt">&lt;/option&gt;</span>
    <span class="nt">&lt;/select&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">onclick=</span><span class="s">"startScanning();"</span><span class="nt">&gt;</span>Start<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"container"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h2 id="添加依赖项">添加依赖项</h2>

<ol>
  <li>
    <p>添加Dynamsoft Document Viewer。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@latest/dist/ddv.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@latest/dist/ddv.css"</span><span class="nt">&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>添加Dynamsoft Document Normalizer（通过引入Capture Vision bundle）。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-bundle@2.6.1000/dist/dcv.bundle.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="初始化sdk">初始化SDK</h2>

<p>使用许可证初始化SDK。可以在<a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&amp;package=cross-platform">此处</a>申请许可证。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">init</span><span class="p">(){</span>
  <span class="c1">// Initialize license</span>
  <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">License</span><span class="p">.</span><span class="nx">LicenseManager</span><span class="p">.</span><span class="nx">initLicense</span><span class="p">(</span>
    <span class="dl">"</span><span class="s2">LICENSE-KEY</span><span class="dl">"</span><span class="p">,</span>
    <span class="kc">true</span>
  <span class="p">);</span>
  <span class="c1">// Initialize DDN</span>
  <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">Core</span><span class="p">.</span><span class="nx">CoreModule</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">([</span><span class="dl">"</span><span class="s2">DDN</span><span class="dl">"</span><span class="p">]);</span>
  <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Core</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">();</span>
  <span class="c1">// Initialize DDV</span>
  <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Core</span><span class="p">.</span><span class="nx">init</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="扫描文档">扫描文档</h2>

<p>Dynamsoft Document Viewer为文档扫描提供了多个组件。</p>

<p>我们将使用其<code class="language-plaintext highlighter-rouge">Capture Viewer</code>通过摄像头捕获文档，使用其<code class="language-plaintext highlighter-rouge">Perspective Viewer</code>调整检测到的边界，并使用其<code class="language-plaintext highlighter-rouge">Edit Viewer</code>查看、编辑和保存扫描的文档。</p>

<p>Capture Viewer：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/capture-viewer.jpg" alt="捕获查看器" /></p>

<p>Perspective Viewer：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/perspective-viewer.jpg" alt="透视查看器" /></p>

<p>Edit Viewer：</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/edit-viewer.jpg" alt="编辑查看器" /></p>

<p>以下是用于设置这些组件的代码。</p>

<ol>
  <li>
    <p>初始化Capture Viewer并将其绑定到一个容器。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">captureViewer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">CaptureViewer</span><span class="p">({</span>
    <span class="na">container</span><span class="p">:</span> <span class="dl">"</span><span class="s2">container</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">uiConfig</span><span class="p">:</span> <span class="nx">captureViewerUiConfig</span><span class="p">,</span>
    <span class="na">viewerConfig</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">acceptedPolygonConfidence</span><span class="p">:</span> <span class="mi">60</span><span class="p">,</span>
        <span class="na">enableAutoDetect</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">});</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>初始化Perspective Viewer并将其绑定到一个容器。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">perspectiveViewer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">PerspectiveViewer</span><span class="p">({</span>
    <span class="na">container</span><span class="p">:</span> <span class="dl">"</span><span class="s2">container</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">groupUid</span><span class="p">:</span> <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">groupUid</span><span class="p">,</span>
    <span class="na">uiConfig</span><span class="p">:</span> <span class="nx">perspectiveUiConfig</span><span class="p">,</span>
    <span class="na">viewerConfig</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">scrollToLatest</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">});</span>
<span class="nx">perspectiveViewer</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>初始化Edit Viewer并将其绑定到一个容器。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">editViewer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">EditViewer</span><span class="p">({</span>
    <span class="na">container</span><span class="p">:</span> <span class="dl">"</span><span class="s2">container</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">groupUid</span><span class="p">:</span> <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">groupUid</span><span class="p">,</span>
    <span class="na">uiConfig</span><span class="p">:</span> <span class="nx">editViewerUiConfig</span>
<span class="p">});</span>
<span class="nx">editViewer</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用config对象配置UI。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">captureViewerUiConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
    <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">"</span><span class="s2">column</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-capture-viewer-header-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">{</span>
                    <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">CameraResolution</span><span class="dl">"</span><span class="p">,</span>
                    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-capture-viewer-resolution</span><span class="dl">"</span><span class="p">,</span>
                <span class="p">},</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Flashlight</span><span class="p">,</span>
            <span class="p">],</span>
        <span class="p">},</span>
        <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">MainView</span><span class="p">,</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-capture-viewer-footer-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">AutoDetect</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">AutoCapture</span><span class="p">,</span>
                <span class="p">{</span>
                    <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Capture</span><span class="dl">"</span><span class="p">,</span>
                    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-capture-viewer-captureButton</span><span class="dl">"</span><span class="p">,</span>
                <span class="p">},</span>
                <span class="p">{</span>
                    <span class="c1">// Bind click event to "ImagePreview" element</span>
                    <span class="c1">// The event will be registered later.</span>
                    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">ImagePreview</span><span class="p">,</span>
                    <span class="na">events</span><span class="p">:{</span>
                        <span class="na">click</span><span class="p">:</span> <span class="dl">"</span><span class="s2">showPerspectiveViewer</span><span class="dl">"</span>
                    <span class="p">}</span>
                <span class="p">},</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">CameraConvert</span><span class="p">,</span>
            <span class="p">],</span>
        <span class="p">},</span>
    <span class="p">],</span>
<span class="p">};</span>


<span class="kd">const</span> <span class="nx">perspectiveUiConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
    <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">"</span><span class="s2">column</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-perspective-viewer-header-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">{</span>
                    <span class="c1">// Add a "Back" button in perspective viewer's header and bind the event to go back to capture viewer.</span>
                    <span class="c1">// The event will be registered later.</span>
                    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Button</span><span class="p">,</span>
                    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-button-back</span><span class="dl">"</span><span class="p">,</span>
                    <span class="na">events</span><span class="p">:{</span>
                        <span class="na">click</span><span class="p">:</span> <span class="dl">"</span><span class="s2">backToCaptureViewer</span><span class="dl">"</span>
                    <span class="p">}</span>
                <span class="p">},</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Pagination</span><span class="p">,</span>
                <span class="p">{</span>   
                    <span class="c1">// Bind event for "PerspectiveAll" button to show the edit viewer</span>
                    <span class="c1">// The event will be registered later.</span>
                    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">PerspectiveAll</span><span class="p">,</span>
                    <span class="na">events</span><span class="p">:{</span>
                        <span class="na">click</span><span class="p">:</span> <span class="dl">"</span><span class="s2">showEditViewer</span><span class="dl">"</span>
                    <span class="p">}</span>
                <span class="p">},</span>
            <span class="p">],</span>
        <span class="p">},</span>
        <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">MainView</span><span class="p">,</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-perspective-viewer-footer-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">FullQuad</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">RotateLeft</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">RotateRight</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">DeleteCurrent</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">DeleteAll</span><span class="p">,</span>
            <span class="p">],</span>
        <span class="p">},</span>
    <span class="p">],</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">editViewerUiConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
    <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">"</span><span class="s2">column</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-edit-viewer-mobile</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-edit-viewer-header-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="p">{</span>
                    <span class="c1">// Add a "Back" buttom to header and bind click event to go back to the perspective viewer</span>
                    <span class="c1">// The event will be registered later.</span>
                    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Button</span><span class="p">,</span>
                    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-button-back</span><span class="dl">"</span><span class="p">,</span>
                    <span class="na">events</span><span class="p">:{</span>
                        <span class="na">click</span><span class="p">:</span> <span class="dl">"</span><span class="s2">backToPerspectiveViewer</span><span class="dl">"</span>
                    <span class="p">}</span>
                <span class="p">},</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Pagination</span><span class="p">,</span>
                <span class="p">{</span>
                    <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Button</span><span class="p">,</span>
                    <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-button-menu</span><span class="dl">"</span><span class="p">,</span>
                    <span class="na">events</span><span class="p">:{</span>
                        <span class="na">click</span><span class="p">:</span> <span class="dl">"</span><span class="s2">menu</span><span class="dl">"</span>
                    <span class="p">}</span>
                <span class="p">}</span>
            <span class="p">],</span>
        <span class="p">},</span>
        <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">MainView</span><span class="p">,</span>
        <span class="p">{</span>
            <span class="na">type</span><span class="p">:</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Layout</span><span class="p">,</span>
            <span class="na">className</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ddv-edit-viewer-footer-mobile</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">children</span><span class="p">:</span> <span class="p">[</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">DisplayMode</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">RotateLeft</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Crop</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Filter</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Undo</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Delete</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">Load</span><span class="p">,</span>
                <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">Elements</span><span class="p">.</span><span class="nx">AnnotationSet</span><span class="p">,</span>
            <span class="p">],</span>
        <span class="p">},</span>
    <span class="p">],</span>
<span class="p">};</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>添加一个辅助函数来切换这些Viewer。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Define a function to control the viewers' visibility</span>
<span class="kd">const</span> <span class="nx">switchViewer</span> <span class="o">=</span> <span class="p">(</span><span class="nx">c</span><span class="p">,</span><span class="nx">p</span><span class="p">,</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span>
  <span class="nx">perspectiveViewer</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span>
  <span class="nx">editViewer</span><span class="p">.</span><span class="nx">hide</span><span class="p">();</span>

  <span class="k">if</span><span class="p">(</span><span class="nx">c</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">stop</span><span class="p">();</span>
  <span class="p">}</span>

  <span class="k">if</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span> <span class="nx">perspectiveViewer</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
<span class="p">};</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>为用来控制工作流的按钮注册事件。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Register an event in `perspectiveViewer` to go back the capture viewer</span>
<span class="nx">perspectiveViewer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">backToCaptureViewer</span><span class="dl">"</span><span class="p">,()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">switchViewer</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
    <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">play</span><span class="p">().</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="nx">alert</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nx">message</span><span class="p">)});</span>
<span class="p">});</span>

<span class="c1">// Register an event in `perspectiveViewer` to show the edit viewer</span>
<span class="nx">perspectiveViewer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">showEditViewer</span><span class="dl">"</span><span class="p">,()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">switchViewer</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="p">});</span>
<span class="c1">// Register an event in `editViewer` to go back to the perspective viewer</span>
<span class="nx">editViewer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">"</span><span class="s2">backToPerspectiveViewer</span><span class="dl">"</span><span class="p">,()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">switchViewer</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>定义文档检测处理程序，使用Dynamsoft Document Normalizer进行边缘检测。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Configure document boundaries function</span>
<span class="k">await</span> <span class="nx">initDocDetectModule</span><span class="p">(</span><span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">,</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">CVR</span><span class="p">);</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nx">initDocDetectModule</span><span class="p">(</span><span class="nx">DDV</span><span class="p">,</span> <span class="nx">CVR</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">CVR</span><span class="p">.</span><span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
  <span class="kd">class</span> <span class="nx">DDNNormalizeHandler</span> <span class="kd">extends</span> <span class="nx">DDV</span><span class="p">.</span><span class="nx">DocumentDetect</span> <span class="p">{</span>
    <span class="k">async</span> <span class="nx">detect</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="nx">config</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">router</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">({</span>
          <span class="na">success</span><span class="p">:</span> <span class="kc">false</span>
        <span class="p">});</span>
      <span class="p">};</span>

      <span class="kd">let</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">ratio</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">data</span><span class="p">;</span>

      <span class="k">if</span> <span class="p">(</span><span class="nx">height</span> <span class="o">&gt;</span> <span class="mi">720</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">ratio</span> <span class="o">=</span> <span class="nx">height</span> <span class="o">/</span> <span class="mi">720</span><span class="p">;</span>
        <span class="nx">height</span> <span class="o">=</span> <span class="mi">720</span><span class="p">;</span>
        <span class="nx">width</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">width</span> <span class="o">/</span> <span class="nx">ratio</span><span class="p">);</span>
        <span class="nx">data</span> <span class="o">=</span> <span class="nx">compress</span><span class="p">(</span><span class="nx">image</span><span class="p">.</span><span class="nx">data</span><span class="p">,</span> <span class="nx">image</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">image</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nx">data</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
      <span class="p">}</span>


      <span class="c1">// Define DSImage according to the usage of DDN</span>
      <span class="kd">const</span> <span class="nx">DSImage</span> <span class="o">=</span> <span class="p">{</span>
        <span class="na">bytes</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">data</span><span class="p">),</span>
        <span class="nx">width</span><span class="p">,</span>
        <span class="nx">height</span><span class="p">,</span>
        <span class="na">stride</span><span class="p">:</span> <span class="nx">width</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="c1">//RGBA</span>
        <span class="na">format</span><span class="p">:</span> <span class="mi">10</span> <span class="c1">// IPF_ABGR_8888</span>
      <span class="p">};</span>

      <span class="c1">// Use DDN normalized module</span>
      <span class="kd">const</span> <span class="nx">results</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">router</span><span class="p">.</span><span class="nx">capture</span><span class="p">(</span><span class="nx">DSImage</span><span class="p">,</span> <span class="dl">'</span><span class="s1">DetectDocumentBoundaries_Default</span><span class="dl">'</span><span class="p">);</span>

      <span class="c1">// Filter the results and generate corresponding return values</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">results</span><span class="p">.</span><span class="nx">items</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">({</span>
          <span class="na">success</span><span class="p">:</span> <span class="kc">false</span>
        <span class="p">});</span>
      <span class="p">};</span>

      <span class="kd">const</span> <span class="nx">quad</span> <span class="o">=</span> <span class="p">[];</span>
      <span class="nx">results</span><span class="p">.</span><span class="nx">items</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">location</span><span class="p">.</span><span class="nx">points</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">p</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">quad</span><span class="p">.</span><span class="nx">push</span><span class="p">([</span><span class="nx">p</span><span class="p">.</span><span class="nx">x</span> <span class="o">*</span> <span class="nx">ratio</span><span class="p">,</span> <span class="nx">p</span><span class="p">.</span><span class="nx">y</span> <span class="o">*</span> <span class="nx">ratio</span><span class="p">]);</span>
      <span class="p">});</span>

      <span class="kd">const</span> <span class="nx">detectResult</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">processDetectResult</span><span class="p">({</span>
        <span class="na">location</span><span class="p">:</span> <span class="nx">quad</span><span class="p">,</span>
        <span class="na">width</span><span class="p">:</span> <span class="nx">image</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
        <span class="na">height</span><span class="p">:</span> <span class="nx">image</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
        <span class="nx">config</span>
      <span class="p">});</span>
      <span class="k">return</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">detectResult</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="nx">DDV</span><span class="p">.</span><span class="nx">setProcessingHandler</span><span class="p">(</span><span class="dl">'</span><span class="s1">documentBoundariesDetect</span><span class="dl">'</span><span class="p">,</span> <span class="k">new</span> <span class="nx">DDNNormalizeHandler</span><span class="p">())</span>
<span class="p">}</span>

<span class="c1">//compress the video frames</span>
<span class="kd">function</span> <span class="nx">compress</span><span class="p">(</span>
    <span class="nx">imageData</span><span class="p">,</span>
    <span class="nx">imageWidth</span><span class="p">,</span>
    <span class="nx">imageHeight</span><span class="p">,</span>
    <span class="nx">newWidth</span><span class="p">,</span>
    <span class="nx">newHeight</span><span class="p">,</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">source</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
  <span class="k">try</span> <span class="p">{</span>
      <span class="nx">source</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8ClampedArray</span><span class="p">(</span><span class="nx">imageData</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">source</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">imageData</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="kd">const</span> <span class="nx">scaleW</span> <span class="o">=</span> <span class="nx">newWidth</span> <span class="o">/</span> <span class="nx">imageWidth</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">scaleH</span> <span class="o">=</span> <span class="nx">newHeight</span> <span class="o">/</span> <span class="nx">imageHeight</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">targetSize</span> <span class="o">=</span> <span class="nx">newWidth</span> <span class="o">*</span> <span class="nx">newHeight</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">targetMemory</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">ArrayBuffer</span><span class="p">(</span><span class="nx">targetSize</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">distData</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>

  <span class="k">try</span> <span class="p">{</span>
      <span class="nx">distData</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8ClampedArray</span><span class="p">(</span><span class="nx">targetMemory</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">targetSize</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">distData</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint8Array</span><span class="p">(</span><span class="nx">targetMemory</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">targetSize</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="kd">const</span> <span class="nx">filter</span> <span class="o">=</span> <span class="p">(</span><span class="nx">distCol</span><span class="p">,</span> <span class="nx">distRow</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">srcCol</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">imageWidth</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">distCol</span> <span class="o">/</span> <span class="nx">scaleW</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">srcRow</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">imageHeight</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">distRow</span> <span class="o">/</span> <span class="nx">scaleH</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">intCol</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">srcCol</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">intRow</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">srcRow</span><span class="p">);</span>

      <span class="kd">let</span> <span class="nx">distI</span> <span class="o">=</span> <span class="p">(</span><span class="nx">distRow</span> <span class="o">*</span> <span class="nx">newWidth</span><span class="p">)</span> <span class="o">+</span> <span class="nx">distCol</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">srcI</span> <span class="o">=</span> <span class="p">(</span><span class="nx">intRow</span> <span class="o">*</span> <span class="nx">imageWidth</span><span class="p">)</span> <span class="o">+</span> <span class="nx">intCol</span><span class="p">;</span>

      <span class="nx">distI</span> <span class="o">*=</span> <span class="mi">4</span><span class="p">;</span>
      <span class="nx">srcI</span> <span class="o">*=</span> <span class="mi">4</span><span class="p">;</span>

      <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;=</span> <span class="mi">3</span><span class="p">;</span> <span class="nx">j</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">distData</span><span class="p">[</span><span class="nx">distI</span> <span class="o">+</span> <span class="nx">j</span><span class="p">]</span> <span class="o">=</span> <span class="nx">source</span><span class="p">[</span><span class="nx">srcI</span> <span class="o">+</span> <span class="nx">j</span><span class="p">];</span>
      <span class="p">}</span>
  <span class="p">};</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">col</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">col</span> <span class="o">&lt;</span> <span class="nx">newWidth</span><span class="p">;</span> <span class="nx">col</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">row</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">row</span> <span class="o">&lt;</span> <span class="nx">newHeight</span><span class="p">;</span> <span class="nx">row</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">filter</span><span class="p">(</span><span class="nx">col</span><span class="p">,</span> <span class="nx">row</span><span class="p">);</span>
      <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">distData</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用所选摄像头和指定的分辨率启动文档扫描工作流。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">startScanning</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">selectedCamera</span> <span class="o">=</span> <span class="nx">cameras</span><span class="p">[</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">select-camera</span><span class="dl">"</span><span class="p">).</span><span class="nx">selectedIndex</span><span class="p">];</span>
  <span class="k">await</span> <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">selectCamera</span><span class="p">(</span><span class="nx">selectedCamera</span><span class="p">.</span><span class="nx">deviceId</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">selectedResolution</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">select-resolution</span><span class="dl">"</span><span class="p">).</span><span class="nx">selectedOptions</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">width</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">selectedResolution</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">x</span><span class="dl">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]);</span>
  <span class="kd">let</span> <span class="nx">height</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">selectedResolution</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">"</span><span class="s2">x</span><span class="dl">"</span><span class="p">)[</span><span class="mi">1</span><span class="p">]);</span>
  <span class="nx">captureViewer</span><span class="p">.</span><span class="nx">play</span><span class="p">({</span>
    <span class="na">resolution</span><span class="p">:</span> <span class="p">[</span><span class="nx">width</span><span class="p">,</span><span class="nx">height</span><span class="p">],</span>
  <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">alert</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nx">message</span><span class="p">)</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="调整图像大小">调整图像大小</h2>

<p>扫描的文档图像的尺寸可能不符合标准比例，例如A4的210毫米x297毫米。</p>

<p>可以使用Canvas调整图像的大小。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">pageWidth</span> <span class="o">=</span> <span class="mi">210</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">pageHeight</span> <span class="o">=</span> <span class="mi">297</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">DPI</span> <span class="o">=</span> <span class="mi">300</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">pageWidthPx</span> <span class="o">=</span> <span class="nx">pageWidth</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span> <span class="c1">//convert to pixel</span>
<span class="kd">let</span> <span class="nx">pageHeightPx</span> <span class="o">=</span> <span class="nx">pageHeight</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">docImageBlob</span> <span class="o">=</span> <span class="p">(</span><span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">getPageData</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">)).</span><span class="nx">display</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">img</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">img</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">img</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="k">async</span> <span class="kd">function</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">resizedDocBlob</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">resizeImage</span><span class="p">(</span><span class="nx">img</span><span class="p">,</span><span class="nx">docWidthPx</span><span class="p">,</span><span class="nx">docHeightPx</span><span class="p">);</span>
  <span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">updatePage</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">,</span> <span class="nx">resizedDocBlob</span><span class="p">);</span>
<span class="p">};</span>
<span class="nx">img</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">docImageBlob</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">resizeImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">width</span><span class="p">;</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">height</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
    <span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">toBlob</span><span class="p">(</span><span class="nx">blob</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="nx">resolve</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>
    <span class="p">});</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>在某些情况下，扫描的文件较小，比如名片，然后我们希望将其打印在一张A4纸上。这时，我们可以先创建一个空白页面，然后在其上添加调整好大小的文档图像。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">docWidth</span> <span class="o">=</span> <span class="mi">89</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">docHeight</span> <span class="o">=</span> <span class="mi">51</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">pageWidth</span> <span class="o">=</span> <span class="mi">210</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">pageHeight</span> <span class="o">=</span> <span class="mi">297</span><span class="p">;</span> <span class="c1">//mm</span>
<span class="kd">let</span> <span class="nx">DPI</span> <span class="o">=</span> <span class="mi">300</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">pageWidthPx</span> <span class="o">=</span> <span class="nx">pageWidth</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span> <span class="c1">//convert to pixel</span>
<span class="kd">let</span> <span class="nx">pageHeightPx</span> <span class="o">=</span> <span class="nx">pageHeight</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">docWidthPx</span> <span class="o">=</span> <span class="nx">docWidth</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">docHeightPx</span> <span class="o">=</span> <span class="nx">docHeight</span> <span class="o">*</span> <span class="nx">DPI</span> <span class="o">/</span> <span class="mf">25.4</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">pageBlob</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">createEmptyPage</span><span class="p">(</span><span class="nx">pageWidthPx</span><span class="p">,</span><span class="nx">pageHeightPx</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">docImageBlob</span> <span class="o">=</span> <span class="p">(</span><span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">getPageData</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">)).</span><span class="nx">display</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">img</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">img</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">img</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="k">async</span> <span class="kd">function</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">resizedDocBlob</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">resizeImage</span><span class="p">(</span><span class="nx">img</span><span class="p">,</span><span class="nx">docWidthPx</span><span class="p">,</span><span class="nx">docHeightPx</span><span class="p">);</span>
  <span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">updatePage</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">,</span> <span class="nx">pageBlob</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">newPageData</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">getPageData</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">ratio</span> <span class="o">=</span> <span class="nx">newPageData</span><span class="p">.</span><span class="nx">cropBox</span><span class="p">.</span><span class="nx">width</span> <span class="o">/</span> <span class="nx">newPageData</span><span class="p">.</span><span class="nx">display</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">rect</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">x</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span>
    <span class="na">y</span><span class="p">:</span> <span class="mi">50</span><span class="p">,</span>
    <span class="na">width</span><span class="p">:</span> <span class="nx">docWidthPx</span> <span class="o">*</span> <span class="nx">ratio</span><span class="p">,</span>
    <span class="na">height</span><span class="p">:</span> <span class="nx">docHeightPx</span> <span class="o">*</span> <span class="nx">ratio</span>
  <span class="p">};</span>
  <span class="kd">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">x</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span>
    <span class="na">y</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span>
    <span class="na">width</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
    <span class="na">height</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
    <span class="na">stamp</span><span class="p">:</span> <span class="nx">resizedDocBlob</span>
  <span class="p">};</span>
  <span class="kd">const</span> <span class="nx">stamp</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">annotationManager</span><span class="p">.</span><span class="nx">createAnnotation</span><span class="p">(</span><span class="nx">pageUid</span><span class="p">,</span> <span class="dl">"</span><span class="s2">stamp</span><span class="dl">"</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span> <span class="c1">//insert the document image as an annotation so that we can move it around</span>
<span class="p">}</span>
<span class="nx">img</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">docImageBlob</span><span class="p">);</span>

<span class="kd">function</span> <span class="nx">createEmptyPage</span><span class="p">(</span><span class="nx">width</span><span class="p">,</span><span class="nx">height</span><span class="p">){</span>
  <span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">width</span><span class="p">;</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">height</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
    <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">white</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="nx">width</span><span class="p">,</span><span class="nx">height</span><span class="p">);</span>
    <span class="nx">canvas</span><span class="p">.</span><span class="nx">toBlob</span><span class="p">(</span><span class="nx">blob</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="nx">resolve</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>
    <span class="p">});</span>  
  <span class="p">})</span>
<span class="p">}</span>
</code></pre></div></div>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/resized-image.jpg" alt="调整后的图像" /></p>

<h2 id="应用图像滤镜">应用图像滤镜</h2>

<p>还需要漂白图像以便打印。我们可以使用Dynamsoft Document Viewer的Edit Viewer应用图像滤镜。</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/image-filters.jpg" alt="图像滤镜" /></p>

<p>下面是设置图像滤镜的代码：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Configure image filter feature which is in edit viewer</span>
<span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">setProcessingHandler</span><span class="p">(</span><span class="dl">"</span><span class="s2">imageFilter</span><span class="dl">"</span><span class="p">,</span> <span class="k">new</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DDV</span><span class="p">.</span><span class="nx">ImageFilter</span><span class="p">());</span>
</code></pre></div></div>

<p>还可以定义自己的图像滤镜。可以查看<a href="https://www.dynamsoft.com/codepool/apply-image-filter-to-photo-javascript.html">此博客</a>以了解更多信息。</p>

<h2 id="保存为pdf">保存为PDF</h2>

<p>使用指定的页面类型将文档另存为PDF：</p>

<p>HTML：</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;label&gt;</span>
  Page Size:
  <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"page-size-select"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"page/default"</span><span class="nt">&gt;</span>Default<span class="nt">&lt;/option&gt;</span>
    <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"page/a4"</span><span class="nt">&gt;</span>A4<span class="nt">&lt;/option&gt;</span>
    <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"page/a3"</span><span class="nt">&gt;</span>A3<span class="nt">&lt;/option&gt;</span>
    <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">"page/letter"</span><span class="nt">&gt;</span>Letter<span class="nt">&lt;/option&gt;</span>
  <span class="nt">&lt;/select&gt;</span>
<span class="nt">&lt;/label&gt;</span>
</code></pre></div></div>

<p>JavaScript：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">downloadAsPDF</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">pageSize</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">page-size-select</span><span class="dl">"</span><span class="p">).</span><span class="nx">selectedOptions</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">pdfSettings</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">compression</span><span class="p">:</span> <span class="dl">"</span><span class="s2">pdf/jpeg</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">pageType</span><span class="p">:</span> <span class="nx">pageSize</span>
  <span class="p">};</span>
  <span class="kd">const</span> <span class="nx">blob</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">editViewer</span><span class="p">.</span><span class="nx">currentDocument</span><span class="p">.</span><span class="nx">saveToPdf</span><span class="p">(</span><span class="nx">pdfSettings</span><span class="p">);</span>
  <span class="nx">downloadBlob</span><span class="p">(</span><span class="nx">blob</span><span class="p">,</span><span class="dl">"</span><span class="s2">document.pdf</span><span class="dl">"</span><span class="p">);</span>
<span class="p">};</span>

<span class="kd">function</span> <span class="nx">downloadBlob</span><span class="p">(</span><span class="nx">blob</span><span class="p">,</span><span class="nx">filename</span><span class="p">){</span>
  <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">blob</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="nx">url</span><span class="p">;</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">download</span> <span class="o">=</span> <span class="nx">filename</span><span class="p">;</span>
  <span class="nx">a</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="打印文档">打印文档</h2>

<p>使用连接的打印机打印PDF文档。</p>

<p>在手机上，我们主要使用无线打印。</p>

<p>在iOS设备上，我们可以使用AirPrint。</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/airprint.jpg" alt="airprint" /></p>

<p>在安卓设备上，我们可以使用Mopria。</p>

<p><img src="https://devblogs.damingsoft.com/album/2025/03/scan-and-print/mopria.jpg" alt="mopria" /></p>

<h2 id="源代码">源代码</h2>

<p>欢迎下载源代码并尝试使用。</p>

<p><a href="https://github.com/tony-xlh/scan-and-print-document-javascript/">https://github.com/tony-xlh/scan-and-print-document-javascript/</a></p>]]></content><author><name>大名维森软件</name></author><category term="文档扫描" /><summary type="html"><![CDATA[本文介绍了如何构建一个网页应用，在手机上扫描和打印文档。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">vCard名片二维码在前端的生成与扫描</title><link href="https://devblogs.damingsoft.com/vcard-qrcode-generation-and-scanning-javascript/" rel="alternate" type="text/html" title="vCard名片二维码在前端的生成与扫描" /><published>2025-03-12T01:31:53+00:00</published><updated>2025-03-12T01:31:53+00:00</updated><id>https://devblogs.damingsoft.com/vcard-qrcode-generation-and-scanning-javascript</id><content type="html" xml:base="https://devblogs.damingsoft.com/vcard-qrcode-generation-and-scanning-javascript/"><![CDATA[<style>
img {
  max-height:400px;
}
</style>

<p>vCard又名VCF（“虚拟联系人文件”），是电子名片的文件格式标准。以下是vCard文件内容的一个示例：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BEGIN:VCARD
VERSION:4.0
FN:Simon Perreault
N:Perreault;Simon;;;ing. jr,M.Sc.
BDAY:--0203
GENDER:M
EMAIL;TYPE=work:simon.perreault@viagenie.ca
END:VCARD
</code></pre></div></div>

<p>vCard可通过电子邮件、聊天应用、NFC或网络链接等方式进行分享。不过更方便的方法是扫描二维码。例如用iPhone的相机应用扫描以下二维码，会提示添加联系人。</p>

<p><img src="/album/2025/03/vcard-qr-code.jpg" alt="vcard二维码" /></p>

<p><img src="/album/2025/03/vcard-qr-code-scanning-with-ios-camera.jpg" alt="vcard二维码iOS摄像头扫描" /></p>

<p>在本文中，我们将构建一个用于生成vCard二维码的HTML页面，以及另一个基于<a href="https://www.dynamsoft.com/barcode-reader/overview/">Dynamsoft Barcode Reader</a> SDK扫描二维码的页面。我们自己编写一个扫描vCard的应用的好处是，可以与Salesforce和Dynamics 365等CRM系统集成，方便管理联系人。</p>

<p>演示视频：</p>

<video src="https://github.com/user-attachments/assets/7cf4e3f4-6c84-4fce-bfd9-e204c2c12fe8" controls="controls" muted="muted" style="max-height:640px; min-height: 200px; max-width: 100%;"></video>

<h2 id="生成vcard二维码">生成vCard二维码</h2>

<p>我们将使用两个第三方库：</p>

<ul>
  <li><a href="https://www.npmjs.com/package/vcard-creator">vCard creator</a>，用于vCard生成</li>
  <li><a href="https://github.com/kazuhikoarase/qrcode-generator/tree/master/js">qrcode generator</a>用于生成二维码</li>
</ul>

<ol>
  <li>
    <p>创建一个新的HTML文件并引用上述库。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>vCard QR Code Generator<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
  <span class="nt">&lt;/style&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"qrcode.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"module"</span><span class="nt">&gt;</span>
    <span class="k">import</span> <span class="nx">VCard</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">https://cdn.skypack.dev/vcard-creator</span><span class="dl">'</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">VCard</span> <span class="o">=</span> <span class="nx">VCard</span><span class="p">;</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h2&gt;</span>vCard QR Code Generator<span class="nt">&lt;/h2&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;script&gt;</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>创建一个表单来输入联系人信息。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"form"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    First name:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"firstName"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Last name:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"lastName"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Phone:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"phone"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Email:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"email"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Company:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"company"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Job title:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"jobTitle"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Website:
    <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"website"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"generateButton"</span><span class="nt">&gt;</span>Generate<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用输入的信息生成二维码。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"placeHolder"</span><span class="nt">&gt;&lt;/div&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">generateButton</span><span class="dl">"</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span><span class="nx">generateQRCode</span><span class="p">);</span>
<span class="kd">function</span> <span class="nx">generateQRCode</span><span class="p">(){</span>
  <span class="kd">const</span> <span class="nx">card</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VCard</span><span class="p">()</span>
  <span class="kd">const</span> <span class="nx">firstName</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">firstName</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">lastName</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">lastName</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">phone</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">phone</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">company</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">company</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">jobTitle</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">jobTitle</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">website</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">website</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">email</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">email</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">;</span>
  <span class="nx">card</span><span class="p">.</span><span class="nx">addName</span><span class="p">(</span><span class="nx">lastName</span><span class="p">,</span> <span class="nx">firstName</span><span class="p">,</span> <span class="dl">""</span><span class="p">,</span> <span class="dl">""</span><span class="p">,</span> <span class="dl">""</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">addCompany</span><span class="p">(</span><span class="nx">company</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">addJobtitle</span><span class="p">(</span><span class="nx">jobTitle</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">addEmail</span><span class="p">(</span><span class="nx">email</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">addPhoneNumber</span><span class="p">(</span><span class="nx">phone</span><span class="p">,</span> <span class="dl">'</span><span class="s1">WORK</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">addURL</span><span class="p">(</span><span class="nx">website</span><span class="p">)</span>
  <span class="kd">const</span> <span class="nx">vcf</span> <span class="o">=</span> <span class="nx">card</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">vcf</span><span class="p">);</span>
  <span class="nx">generateQR</span><span class="p">(</span><span class="nx">vcf</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">generateQR</span><span class="p">(</span><span class="nx">content</span><span class="p">){</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">typeNumber</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">errorCorrectionLevel</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">L</span><span class="dl">'</span><span class="p">;</span>
    <span class="kd">var</span> <span class="nx">qr</span> <span class="o">=</span> <span class="nx">qrcode</span><span class="p">(</span><span class="nx">typeNumber</span><span class="p">,</span> <span class="nx">errorCorrectionLevel</span><span class="p">);</span>
    <span class="nx">qr</span><span class="p">.</span><span class="nx">addData</span><span class="p">(</span><span class="nx">content</span><span class="p">);</span>
    <span class="nx">qr</span><span class="p">.</span><span class="nx">make</span><span class="p">();</span>
    <span class="kd">var</span> <span class="nx">placeHolder</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">placeHolder</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">placeHolder</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">qr</span><span class="p">.</span><span class="nx">createSvgTag</span><span class="p">();</span>  
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">alert</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>我们还可以使用现有的vcf文件生成二维码。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"generateWithExistingButton"</span><span class="nt">&gt;</span>Generate with an existing vcf file<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;input</span> <span class="na">style=</span><span class="s">"display:none;"</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">id=</span><span class="s">"file"</span> <span class="na">onchange=</span><span class="s">"loadFromFile();"</span> <span class="na">accept=</span><span class="s">".vcf"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">generateWithExistingButton</span><span class="dl">"</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span><span class="kd">function</span><span class="p">(){</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">file</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="p">});</span>

<span class="kd">function</span> <span class="nx">loadFromFile</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">fileInput</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">file</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">fileInput</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="kd">let</span> <span class="nx">file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
  <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span>
  <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
    <span class="nx">generateQR</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">);</span>
  <span class="p">};</span>
  <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="dl">'</span><span class="s1">oops, something went wrong.</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">};</span>
  <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsText</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>请注意，一个二维码只能包含约2KB的数据。如果需要传输更多数据，可以尝试<a href="https://www.dynamsoft.com/codepool/transfer-data-with-animated-qr-codes.html">动态二维码</a>的方法。</p>

<h2 id="扫描vcard二维码">扫描vCard二维码</h2>

<p>接下来，创建一个页面来扫描vCard二维码。</p>

<h3 id="新建html文件">新建HTML文件</h3>

<p>创建一个包含以下内容的新HTML文件：</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>vCard QR Code Scanner<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
  <span class="nt">h2</span> <span class="p">{</span>
    <span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">#app</span> <span class="p">{</span>
    <span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
    <span class="nl">flex-direction</span><span class="p">:</span> <span class="n">column</span><span class="p">;</span>
    <span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">#cameraView</span> <span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="m">60vh</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h2&gt;</span>vCard Scanner<span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"startScanBtn"</span><span class="nt">&gt;</span>Start Scanning<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"readFromImageBtn"</span><span class="nt">&gt;</span>Read from an Image<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">id=</span><span class="s">"fileInput"</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">style=</span><span class="s">"display:none;"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"status"</span><span class="nt">&gt;</span>Loading...<span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"cameraView"</span><span class="nt">&gt;&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"result"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h3 id="添加dynamsoft-barcode-reader">添加Dynamsoft Barcode Reader</h3>

<p>添加下面的代码以引入Dynamsoft Barcode Reader：</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@10.4.2000/dist/dbr.bundle.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
</code></pre></div></div>

<p>然后，初始化Dynamsoft Barcode Reader。</p>

<ol>
  <li>
    <p>初始化许可证。可以在<a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&amp;package=cross-platform">此处</a>申请许可证。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">License</span><span class="p">.</span><span class="nx">LicenseManager</span><span class="p">.</span><span class="nx">initLicense</span><span class="p">(</span><span class="dl">"</span><span class="s2">LICENSE-KEY</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>加载WASM文件。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">Core</span><span class="p">.</span><span class="nx">CoreModule</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">([</span><span class="dl">"</span><span class="s2">dbr</span><span class="dl">"</span><span class="p">]);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>创建capture vision router实例以调用Dynamsoft Barcode Reader。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">cvRouter</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">CVR</span><span class="p">.</span><span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="打开摄像头扫描">打开摄像头扫描</h3>

<ol>
  <li>
    <p>初始化Camera Enhancer并将其组件绑定到一个容器。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">cameraView</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DCE</span><span class="p">.</span><span class="nx">CameraView</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">cameraEnhancer</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DCE</span><span class="p">.</span><span class="nx">CameraEnhancer</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">(</span><span class="nx">cameraView</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#cameraView</span><span class="dl">"</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">cameraView</span><span class="p">.</span><span class="nx">getUIElement</span><span class="p">());</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>使用Camera Enhancer作为capture vision router的输入，这样它就可以从摄像头获取帧来读取条形码。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cvRouter</span><span class="p">.</span><span class="nx">setInput</span><span class="p">(</span><span class="nx">cameraEnhancer</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>添加结果接收器以接收扫描结果。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cvRouter</span><span class="p">.</span><span class="nx">addResultReceiver</span><span class="p">({</span> <span class="na">onDecodedBarcodesReceived</span><span class="p">:</span> <span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">displayResults</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
<span class="p">}});</span>

<span class="kd">function</span> <span class="nx">displayResults</span><span class="p">(</span><span class="nx">result</span><span class="p">){</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">barcodeResultItems</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">container</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">result</span><span class="dl">"</span><span class="p">);</span>
    <span class="kd">let</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">barcodeResultItems</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>单击扫描按钮后开始扫描。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">templateName</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">ReadSingleBarcode</span><span class="dl">"</span>
<span class="k">await</span> <span class="nx">cameraEnhancer</span><span class="p">.</span><span class="nx">open</span><span class="p">();</span>
<span class="k">await</span> <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">startCapturing</span><span class="p">(</span><span class="nx">templateName</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="解析vcard">解析vCard</h3>

<p>接下来，解析vCard的文本并显示联系人信息。</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- https://github.com/Heymdall/vcard/ --&gt;</span>
<span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"vcf.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="kd">function</span> <span class="nx">formatedContactInfo</span><span class="p">(</span><span class="nx">barcodeText</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">parsed</span> <span class="o">=</span> <span class="nx">parse</span><span class="p">(</span><span class="nx">barcodeText</span><span class="p">);</span>
    <span class="kd">let</span> <span class="nx">presetKeys</span> <span class="o">=</span> <span class="p">{</span><span class="dl">"</span><span class="s2">fn</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">Full name</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">org</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">Organization</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">title</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">Job Title</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">URL</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">tel</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">Tel</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">email</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">Email</span><span class="dl">"</span><span class="p">};</span>
    <span class="kd">let</span> <span class="nx">str</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">index</span> <span class="o">&lt;</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">presetKeys</span><span class="p">).</span><span class="nx">length</span><span class="p">;</span> <span class="nx">index</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">presetKeys</span><span class="p">)[</span><span class="nx">index</span><span class="p">];</span>
      <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">presetKeys</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
      <span class="k">if</span> <span class="p">(</span><span class="nx">key</span> <span class="k">in</span> <span class="nx">parsed</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">let</span> <span class="nx">appendedValue</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
        <span class="kd">let</span> <span class="nx">valueArray</span> <span class="o">=</span> <span class="nx">parsed</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
        <span class="nx">valueArray</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">valueObject</span> <span class="o">=&gt;</span> <span class="p">{</span>
          <span class="nx">appendedValue</span> <span class="o">=</span> <span class="nx">appendedValue</span> <span class="o">+</span> <span class="nx">valueObject</span><span class="p">.</span><span class="nx">value</span> <span class="o">+</span> <span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>
        <span class="p">});</span>
        <span class="nx">str</span> <span class="o">=</span> <span class="nx">str</span> <span class="o">+</span> <span class="nx">value</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">: </span><span class="dl">"</span> <span class="o">+</span><span class="nx">appendedValue</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">+</span> <span class="dl">"</span><span class="se">\n</span><span class="dl">"</span><span class="p">;</span>
      <span class="p">}</span>
    <span class="p">}</span>
    <span class="nx">vCardContent</span> <span class="o">=</span> <span class="nx">barcodeText</span><span class="p">;</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="dl">"</span><span class="s2">buttons</span><span class="dl">"</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">hidden</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">str</span><span class="p">;</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">Invalid vCard.</span><span class="dl">"</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<h3 id="下载为vcf文件">下载为VCF文件</h3>

<p>我们可以将vCard下载为<code class="language-plaintext highlighter-rouge">.vcf</code>文件。它可以通过系统的联系人应用程序打开。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">downloadText</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">filename</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">scanned.vcf</span><span class="dl">'</span><span class="p">;</span>
  <span class="kd">let</span> <span class="nx">link</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">link</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">;</span>
  <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">target</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">_blank</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">href</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">data:text/vcard;charset=utf-8,</span><span class="dl">'</span> <span class="o">+</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">vCardContent</span><span class="p">));</span>
  <span class="nx">link</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">download</span><span class="dl">'</span><span class="p">,</span> <span class="nx">filename</span><span class="p">);</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
  <span class="nx">link</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">link</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="读取图像中的二维码">读取图像中的二维码</h3>

<p>除了实时扫描，我们还可以读取图像中的vCard二维码。</p>

<p>添加用于选择图像文件的事件。选择图像后，使用<code class="language-plaintext highlighter-rouge">capture</code>方法识别其中的条码。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">readFromImageBtn</span><span class="dl">"</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span><span class="kd">function</span><span class="p">(){</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">initialized</span><span class="p">)</span> <span class="p">{</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">fileInput</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
  <span class="p">}</span><span class="k">else</span><span class="p">{</span>
    <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Please wait for the initialization.</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">});</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">fileInput</span><span class="dl">"</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">change</span><span class="dl">"</span><span class="p">,</span><span class="k">async</span> <span class="kd">function</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">files</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">fileInput</span><span class="dl">"</span><span class="p">).</span><span class="nx">files</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span><span class="o">&gt;</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">file</span>  <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">capture</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span><span class="nx">templateName</span><span class="p">);</span>
    <span class="nx">displayResults</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div></div>

<p>好了，demo已经编写好了。</p>

<h2 id="源代码">源代码</h2>

<p>获取源代码来自己试用一下吧：</p>

<ul>
  <li>生成器：<a href="https://github.com/tony-xlh/online-vcard-qr-code-generator">https://github.com/tony-xlh/online-vcard-qr-code-generator</a></li>
  <li>扫描器：<a href="https://github.com/tony-xlh/vcard-qr-code-scanner">https://github.com/tony-xlh/vcard-qr-code-scanner</a></li>
</ul>]]></content><author><name>大名维森软件</name></author><category term="条码识别" /><summary type="html"><![CDATA[本文讨论了如何使用JavaScript构建HTML页面以生成和扫描vCard二维码。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">JavaScript自适应阈值图像二值化</title><link href="https://devblogs.damingsoft.com/adaptive-thresholding-javascript/" rel="alternate" type="text/html" title="JavaScript自适应阈值图像二值化" /><published>2025-03-03T09:00:53+00:00</published><updated>2025-03-03T09:00:53+00:00</updated><id>https://devblogs.damingsoft.com/adaptive-thresholding-javascript</id><content type="html" xml:base="https://devblogs.damingsoft.com/adaptive-thresholding-javascript/"><![CDATA[<p>在<a href="https://devblogs.damingsoft.com/convert-image-to-black-white-with-javascript/">上一篇文章</a>中，我们谈到了如何使用固定阈值将图像转换为黑白图像。对于在条码识别等任务中可能出现的光照不均的图像，它的效果并不理想。</p>

<p>例如，如果我们将以下带阴影的二维码用固定阈值进行二值化，部分二维码内容将丢失，导致无法读取。</p>

<p><img src="/album/2025/03/image-processing/barcode-with-shadow.jpg" alt="带阴影的条形码" /></p>

<p><img src="/album/2025/03/image-processing/barcode-with-shadow-simple-thresholding.jpg" alt="带有阴影的条形码-简单阈值处理" /></p>

<p>对于这种情况，我们可以使用自适应阈值二值化来获得理想的结果。这种方法会根据像素的邻近像素来计算每个像素的阈值。</p>

<p><img src="/album/2025/03/image-processing/barcode-with-shadow-adaptive-thresholding.jpg" alt="自适应阈值处理的带阴影条形码" /></p>

<p>在本文中，我们将基于HTML5 Canvas使用JavaScript中实现自适应阈值图像二值化。我们还将探讨如何使用<a href="https://www.dynamsoft.com/barcode-reader/overview/">Dynamsoft Barcode Reader</a>执行此操作。</p>

<p><a href="https://tony-xlh.github.io/adaptive-thresholding-javascript/">在线demo</a></p>

<h2 id="新建html文件">新建HTML文件</h2>

<p>使用以下内容创建一个新的HTML文件，该文件可以选择本地图像并显示。</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>Adaptive Thresholding<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
  <span class="nc">.imageContainer</span> <span class="p">{</span>
    <span class="nl">overflow</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
    <span class="nl">max-width</span><span class="p">:</span> <span class="m">360px</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nc">.imageContainer</span> <span class="nt">img</span><span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nf">#imageHidden</span> <span class="p">{</span>
    <span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"app"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h2&gt;</span>Adaptive Thresholding<span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"loadFileButton"</span><span class="nt">&gt;</span>Load a File<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;input</span> <span class="na">style=</span><span class="s">"display:none;"</span> <span class="na">type=</span><span class="s">"file"</span> <span class="na">id=</span><span class="s">"file"</span> <span class="na">onchange=</span><span class="s">"loadImageFromFile();"</span> <span class="na">accept=</span><span class="s">".jpg,.jpeg,.png,.bmp"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"processButton"</span><span class="nt">&gt;</span>Process<span class="nt">&lt;/button&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"status"</span><span class="nt">&gt;&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"imageContainer"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;img</span> <span class="na">id=</span><span class="s">"image"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;img</span> <span class="na">id=</span><span class="s">"imageHidden"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;pre</span> <span class="na">id=</span><span class="s">"barcodeResult"</span><span class="nt">&gt;&lt;/pre&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;script&gt;</span>
  <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">loadFileButton</span><span class="dl">"</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span><span class="kd">function</span><span class="p">(){</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">file</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
  <span class="p">})</span>
  <span class="kd">function</span> <span class="nx">loadImageFromFile</span><span class="p">(){</span>
    <span class="kd">let</span> <span class="nx">fileInput</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">file</span><span class="dl">"</span><span class="p">);</span>
    <span class="kd">let</span> <span class="nx">files</span> <span class="o">=</span> <span class="nx">fileInput</span><span class="p">.</span><span class="nx">files</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">files</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kd">let</span> <span class="nx">file</span> <span class="o">=</span> <span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="nx">fileReader</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FileReader</span><span class="p">();</span>
    <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">e</span><span class="p">){</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">image</span><span class="dl">"</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">imageHidden</span><span class="dl">"</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">result</span><span class="p">;</span>
    <span class="p">};</span>
    <span class="nx">fileReader</span><span class="p">.</span><span class="nx">onerror</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
      <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="dl">'</span><span class="s1">oops, something went wrong.</span><span class="dl">'</span><span class="p">);</span>
    <span class="p">};</span>
    <span class="nx">fileReader</span><span class="p">.</span><span class="nx">readAsDataURL</span><span class="p">(</span><span class="nx">file</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<h2 id="使用自适应阈值将图像转换为黑白">使用自适应阈值将图像转换为黑白</h2>

<p>接下来，用自适应阈值将图像转换为黑白图像。</p>

<ol>
  <li>
    <p>将图像绘制到Canvas上并获取其图像数据。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">cvs</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">image</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">imageHidden</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">cvs</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">naturalWidth</span><span class="p">;</span>
<span class="nx">cvs</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">image</span><span class="p">.</span><span class="nx">naturalHeight</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">cvs</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">imageData</span> <span class="o">=</span> <span class="nx">ctx</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="nx">cvs</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span><span class="nx">cvs</span><span class="p">.</span><span class="nx">height</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>遍历像素，根据相邻像素计算阈值，并据此修改其像素值。它需要两个额外的参数：块大小和一个常量C。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">adaptiveThreshold</span><span class="p">(</span><span class="nx">imageData</span><span class="p">,</span> <span class="nx">blockSize</span><span class="p">,</span> <span class="nx">C</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ImageData</span><span class="p">(</span><span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">outputData</span> <span class="o">=</span> <span class="nx">output</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">y</span> <span class="o">&lt;</span> <span class="nx">height</span><span class="p">;</span> <span class="nx">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">x</span> <span class="o">&lt;</span> <span class="nx">width</span><span class="p">;</span> <span class="nx">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

      <span class="c1">//local mean</span>
      <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">dy</span> <span class="o">=</span> <span class="o">-</span><span class="nx">blockSize</span><span class="p">;</span> <span class="nx">dy</span> <span class="o">&lt;=</span> <span class="nx">blockSize</span><span class="p">;</span> <span class="nx">dy</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">dx</span> <span class="o">=</span> <span class="o">-</span><span class="nx">blockSize</span><span class="p">;</span> <span class="nx">dx</span> <span class="o">&lt;=</span> <span class="nx">blockSize</span><span class="p">;</span> <span class="nx">dx</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">nx</span> <span class="o">=</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">dx</span><span class="p">;</span>
          <span class="kd">const</span> <span class="nx">ny</span> <span class="o">=</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">dy</span><span class="p">;</span>
          <span class="k">if</span> <span class="p">(</span><span class="nx">nx</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">nx</span> <span class="o">&lt;</span> <span class="nx">width</span> <span class="o">&amp;&amp;</span> <span class="nx">ny</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">ny</span> <span class="o">&lt;</span> <span class="nx">height</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">const</span> <span class="nx">idx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">ny</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">nx</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
            <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span> <span class="c1">//use the red channel as the grayscale value</span>
            <span class="nx">count</span><span class="o">++</span><span class="p">;</span>
          <span class="p">}</span>
        <span class="p">}</span>
      <span class="p">}</span>

      <span class="kd">const</span> <span class="nx">threshold</span> <span class="o">=</span> <span class="p">(</span><span class="nx">sum</span> <span class="o">/</span> <span class="nx">count</span><span class="p">)</span> <span class="o">-</span> <span class="nx">C</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">idx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">y</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">pixelValue</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span>

      <span class="c1">// binarize</span>
      <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span> <span class="o">=</span> <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pixelValue</span> <span class="o">&gt;</span> <span class="nx">threshold</span> <span class="p">?</span> <span class="mi">255</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
      <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span> <span class="c1">// Alpha channel</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">output</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>将更新后的图像数据放回Canvas中，并显示处理后的图像。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">blockSize</span> <span class="o">=</span> <span class="mi">31</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">C</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">newImageData</span> <span class="o">=</span> <span class="nx">adaptiveThreshold</span><span class="p">(</span><span class="nx">ctx</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="nx">cvs</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span><span class="nx">cvs</span><span class="p">.</span><span class="nx">height</span><span class="p">),</span><span class="nx">blockSize</span><span class="p">,</span><span class="nx">C</span><span class="p">);</span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">putImageData</span><span class="p">(</span><span class="nx">newImageData</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">image</span><span class="dl">"</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">cvs</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="dl">"</span><span class="s2">image/jpeg</span><span class="dl">"</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="提高运行效率">提高运行效率</h2>

<p>上述实现的计算复杂度为<code class="language-plaintext highlighter-rouge">O(N*k *k)</code>。N代表像素数，k代表块大小。</p>

<p>我们可以使用积分图将复杂度降低到<code class="language-plaintext highlighter-rouge">O(N)</code>，代码如下：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">adaptiveThresholdWithIntegralImage</span><span class="p">(</span><span class="nx">imageData</span><span class="p">,</span> <span class="nx">blockSize</span><span class="p">,</span> <span class="nx">C</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">imageData</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">output</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ImageData</span><span class="p">(</span><span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">outputData</span> <span class="o">=</span> <span class="nx">output</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>

  <span class="kd">const</span> <span class="nx">integral</span> <span class="o">=</span> <span class="nx">computeIntegralImage</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span>

  <span class="kd">const</span> <span class="nx">halfBlock</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nx">blockSize</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">y</span> <span class="o">&lt;</span> <span class="nx">height</span><span class="p">;</span> <span class="nx">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">x</span> <span class="o">&lt;</span> <span class="nx">width</span><span class="p">;</span> <span class="nx">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">x1</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">x</span> <span class="o">-</span> <span class="nx">halfBlock</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">y1</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">y</span> <span class="o">-</span> <span class="nx">halfBlock</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">x2</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">halfBlock</span><span class="p">,</span> <span class="nx">width</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">y2</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">y</span> <span class="o">+</span> <span class="nx">halfBlock</span><span class="p">,</span> <span class="nx">height</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>

      <span class="kd">const</span> <span class="nx">area</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x2</span> <span class="o">-</span> <span class="nx">x1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="nx">y2</span> <span class="o">-</span> <span class="nx">y1</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">sum</span> <span class="o">=</span> <span class="nx">getAreaSum</span><span class="p">(</span><span class="nx">integral</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">x1</span><span class="p">,</span> <span class="nx">y1</span><span class="p">,</span> <span class="nx">x2</span><span class="p">,</span> <span class="nx">y2</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">threshold</span> <span class="o">=</span> <span class="p">(</span><span class="nx">sum</span> <span class="o">/</span> <span class="nx">area</span><span class="p">)</span> <span class="o">-</span> <span class="nx">C</span><span class="p">;</span>

      <span class="kd">const</span> <span class="nx">idx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">y</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">pixelValue</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span>
      <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span> <span class="o">=</span> <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="nx">pixelValue</span> <span class="o">&gt;</span> <span class="nx">threshold</span> <span class="p">?</span> <span class="mi">255</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
      <span class="nx">outputData</span><span class="p">[</span><span class="nx">idx</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span> <span class="c1">// Alpha channel</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">output</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">computeIntegralImage</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">integral</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Uint32Array</span><span class="p">(</span><span class="nx">width</span> <span class="o">*</span> <span class="nx">height</span><span class="p">);</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">y</span> <span class="o">&lt;</span> <span class="nx">height</span><span class="p">;</span> <span class="nx">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">x</span> <span class="o">&lt;</span> <span class="nx">width</span><span class="p">;</span> <span class="nx">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">idx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">y</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x</span><span class="p">)</span> <span class="o">*</span> <span class="mi">4</span><span class="p">;</span>
      <span class="nx">sum</span> <span class="o">+=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">idx</span><span class="p">];</span>
      <span class="nx">integral</span><span class="p">[</span><span class="nx">y</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="nx">y</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="nx">integral</span><span class="p">[(</span><span class="nx">y</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x</span><span class="p">]</span> <span class="p">:</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="nx">sum</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="nx">integral</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">getAreaSum</span><span class="p">(</span><span class="nx">integral</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">x1</span><span class="p">,</span> <span class="nx">y1</span><span class="p">,</span> <span class="nx">x2</span><span class="p">,</span> <span class="nx">y2</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">x1</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">y1</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="nx">integral</span><span class="p">[(</span><span class="nx">y1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="p">(</span><span class="nx">x1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)]</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">y1</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="nx">integral</span><span class="p">[(</span><span class="nx">y1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x2</span><span class="p">]</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">c</span> <span class="o">=</span> <span class="nx">x1</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">?</span> <span class="nx">integral</span><span class="p">[</span><span class="nx">y2</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="p">(</span><span class="nx">x1</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)]</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">d</span> <span class="o">=</span> <span class="nx">integral</span><span class="p">[</span><span class="nx">y2</span> <span class="o">*</span> <span class="nx">width</span> <span class="o">+</span> <span class="nx">x2</span><span class="p">];</span>
  <span class="k">return</span> <span class="nx">d</span> <span class="o">-</span> <span class="nx">b</span> <span class="o">-</span> <span class="nx">c</span> <span class="o">+</span> <span class="nx">a</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>处理上面的示例图像的时间可以从2000ms缩短到8ms。</p>

<h2 id="dynamsoft-barcode-reader中的自适应阈值处理">Dynamsoft Barcode Reader中的自适应阈值处理</h2>

<p>Dynamsoft Barcode Reader会使用自适应阈值法处理图像来读取条码。</p>

<p>以下是通过它的中间结果接收器获取二值化图像的代码。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">router</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">CVR</span><span class="p">.</span><span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">intermediateResultReceiver</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">CVR</span><span class="p">.</span><span class="nx">IntermediateResultReceiver</span><span class="p">();</span>
<span class="nx">intermediateResultReceiver</span><span class="p">.</span><span class="nx">onBinaryImageUnitReceived</span> <span class="o">=</span> <span class="p">(</span><span class="nx">result</span><span class="p">,</span> <span class="nx">info</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">displayBinarizedImage</span><span class="p">(</span><span class="nx">result</span><span class="p">)</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">intermediateResultManager</span> <span class="o">=</span> <span class="nx">router</span><span class="p">.</span><span class="nx">getIntermediateResultManager</span><span class="p">();</span>
<span class="nx">intermediateResultManager</span><span class="p">.</span><span class="nx">addResultReceiver</span><span class="p">(</span><span class="nx">intermediateResultReceiver</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">router</span><span class="p">.</span><span class="nx">capture</span><span class="p">(</span><span class="nx">image</span><span class="p">,</span><span class="dl">"</span><span class="s2">ReadSingleBarcode</span><span class="dl">"</span><span class="p">);</span> <span class="c1">//start image processing</span>
</code></pre></div></div>

<p>我们可以通过更新它的JSON模板的<code class="language-plaintext highlighter-rouge">BinarizationMode</code>部分来修改自适应阈值的参数。</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"BinarizationMode"</span><span class="p">:</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"BinarizationThreshold"</span><span class="p">:</span><span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"BlockSizeX"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"BlockSizeY"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"EnableFillBinaryVacancy"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"GrayscaleEnhancementModesIndex"</span><span class="p">:</span><span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Mode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"BM_LOCAL_BLOCK"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MorphOperation"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Close"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MorphOperationKernelSizeX"</span><span class="p">:</span><span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MorphOperationKernelSizeY"</span><span class="p">:</span><span class="w"> </span><span class="mi">-1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"MorphShape"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Rectangle"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ThresholdCompensation"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="源代码">源代码</h2>

<p>可以在以下仓库中找到所有代码和在线演示：</p>

<p><a href="https://github.com/tony-xlh/adaptive-thresholding-javascript">https://github.com/tony-xlh/adaptive-thresholding-javascript</a></p>]]></content><author><name>大名维森软件</name></author><category term="条码识别" /><summary type="html"><![CDATA[文章分享了如何在JavaScript中对图像执行自适应阈值二值化处理。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">安卓使用USB摄像头识别护照MRZ</title><link href="https://devblogs.damingsoft.com/usb-camera-android-mrz-scanner/" rel="alternate" type="text/html" title="安卓使用USB摄像头识别护照MRZ" /><published>2025-02-25T02:29:53+00:00</published><updated>2025-02-25T02:29:53+00:00</updated><id>https://devblogs.damingsoft.com/usb-camera-android-mrz-scanner</id><content type="html" xml:base="https://devblogs.damingsoft.com/usb-camera-android-mrz-scanner/"><![CDATA[<p>在<a href="https://www.dynamsoft.com/codepool/usb-camera-android-barcode-scanner.html">上一篇文章</a>中，我们讨论了如何在安卓设备上使用USB摄像头扫描条形码。在本文中，我们将使用<a href="https://www.dynamsoft.com/label-recognition/overview/">Dynamsoft Label Recognizer</a>扫描护照或者身份证上的MRZ（机器可读区域）。</p>

<p>演示视频：</p>

<video src="https://github.com/user-attachments/assets/6b470edb-f582-483f-8986-db0ac16aa147" controls="controls" muted="muted" class="d-block rounded-bottom-2 width-fit" style="max-width:100%;max-height:640px;"></video>

<h2 id="新建项目">新建项目</h2>

<p>打开Android Studio，创建一个Java语言的新项目。</p>

<h2 id="添加依赖项">添加依赖项</h2>

<ol>
  <li>
    <p>添加UVCCamera库。</p>

    <ol>
      <li>
        <p>下载以下aar，并将其放在<code class="language-plaintext highlighter-rouge">app/libs</code>文件夹中。</p>

        <ul>
          <li><a href="https://github.com/o0olele/UVCcamera-Opencv/raw/master/app/libs/libuvccamera.aar">libuvccamera.aar</a></li>
          <li><a href="https://github.com/o0olele/UVCcamera-Opencv/raw/master/app/libs/usbCameraCommon.aar">usbCameraCommon.aar</a></li>
        </ul>
      </li>
      <li>
        <p>在<code class="language-plaintext highlighter-rouge">settings.gradle</code>中，添加以下行：</p>

        <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dependencyResolutionManagement</span> <span class="o">{</span>
    <span class="n">repositories</span> <span class="o">{</span>
        <span class="n">google</span><span class="o">()</span>
        <span class="n">mavenCentral</span><span class="o">()</span>
        <span class="n">maven</span> <span class="o">{</span> <span class="n">url</span> <span class="s1">'https://raw.github.com/saki4510t/libcommon/master/repository/'</span> <span class="o">}</span>
        <span class="n">flatDir</span> <span class="o">{</span>
            <span class="n">dirs</span> <span class="s1">'libs'</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>在应用的<code class="language-plaintext highlighter-rouge">build.gradle</code>中，添加以下几行：</p>

        <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dependencies</span> <span class="o">{</span>
    <span class="n">implementation</span> <span class="nf">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="s1">'libs'</span><span class="o">,</span> <span class="nl">include:</span> <span class="o">[</span><span class="s1">'*.aar'</span><span class="o">,</span><span class="s1">'*.jar'</span><span class="o">])</span>
    <span class="n">implementation</span><span class="o">(</span><span class="s2">"com.serenegiant:common:1.5.20"</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">exclude</span> <span class="nl">module:</span> <span class="s1">'support-v4'</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>此外可能还需要在<code class="language-plaintext highlighter-rouge">gradle.properties</code>中启用Jetifier。</p>

        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>android.enableJetifier=true
</code></pre></div>        </div>
      </li>
    </ol>
  </li>
  <li>
    <p>添加Dynamsoft SDK。</p>

    <ol>
      <li>
        <p>在<code class="language-plaintext highlighter-rouge">settings.gradle</code>中，添加以下行：</p>

        <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dependencyResolutionManagement</span> <span class="o">{</span>
    <span class="n">repositoriesMode</span><span class="o">.</span><span class="na">set</span><span class="o">(</span><span class="n">RepositoriesMode</span><span class="o">.</span><span class="na">FAIL_ON_PROJECT_REPOS</span><span class="o">)</span>
    <span class="n">repositories</span> <span class="o">{</span>
        <span class="n">maven</span> <span class="o">{</span>
            <span class="n">url</span> <span class="s2">"https://download2.dynamsoft.com/maven/aar"</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>在应用的<code class="language-plaintext highlighter-rouge">build.gradle</code>中，添加以下几行：</p>

        <div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dependencies</span> <span class="o">{</span>
    <span class="n">implementation</span> <span class="s2">"com.dynamsoft:dynamsoftcapturevisionbundle:2.6.1002"</span>
    <span class="n">implementation</span> <span class="s1">'com.dynamsoft:dynamsoftmrz:3.4.20'</span>
<span class="o">}</span>
</code></pre></div>        </div>
      </li>
    </ol>
  </li>
</ol>

<h2 id="打开usb摄像头">打开USB摄像头</h2>

<p>让我们执行以下操作以打开连接的USB摄像头。</p>

<h3 id="添加控件">添加控件</h3>

<p>添加用于预览相机的<code class="language-plaintext highlighter-rouge">UVCCameraTextureView</code>和用于选择和打开相机的<code class="language-plaintext highlighter-rouge">ImageButton</code>。</p>

<p>XML：</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;com.serenegiant.widget.UVCCameraTextureView</span>
        <span class="na">android:id=</span><span class="s">"@+id/camera_view"</span>
        <span class="na">android:layout_width=</span><span class="s">"407dp"</span>
        <span class="na">android:layout_height=</span><span class="s">"720dp"</span>
        <span class="na">android:layout_centerInParent=</span><span class="s">"true"</span>
        <span class="na">app:layout_constraintBottom_toBottomOf=</span><span class="s">"parent"</span>
        <span class="na">app:layout_constraintEnd_toEndOf=</span><span class="s">"parent"</span>
        <span class="na">app:layout_constraintStart_toStartOf=</span><span class="s">"parent"</span>
        <span class="na">app:layout_constraintTop_toTopOf=</span><span class="s">"parent"</span> <span class="nt">/&gt;</span>

<span class="nt">&lt;ImageButton</span>
    <span class="na">android:id=</span><span class="s">"@+id/imageButton"</span>
    <span class="na">android:layout_width=</span><span class="s">"50dp"</span>
    <span class="na">android:layout_height=</span><span class="s">"50dp"</span>
    <span class="na">android:layout_marginBottom=</span><span class="s">"16dp"</span>
    <span class="na">android:background=</span><span class="s">"@android:color/transparent"</span>
    <span class="na">android:scaleType=</span><span class="s">"fitCenter"</span>
    <span class="na">android:src=</span><span class="s">"@android:drawable/ic_menu_camera"</span>
    <span class="na">app:layout_constraintBottom_toBottomOf=</span><span class="s">"parent"</span>
    <span class="na">app:layout_constraintEnd_toEndOf=</span><span class="s">"parent"</span>
    <span class="na">app:layout_constraintStart_toStartOf=</span><span class="s">"parent"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>在MainActivity中：</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">CameraViewInterface</span> <span class="n">mUVCCameraView</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">ImageButton</span> <span class="n">mCameraButton</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="n">mCameraButton</span> <span class="o">=</span> <span class="n">findViewById</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">imageButton</span><span class="o">);</span>
    <span class="kd">final</span> <span class="nc">View</span> <span class="n">view</span> <span class="o">=</span> <span class="n">findViewById</span><span class="o">(</span><span class="no">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">camera_view</span><span class="o">);</span>
    <span class="n">mUVCCameraView</span> <span class="o">=</span> <span class="o">(</span><span class="nc">CameraViewInterface</span><span class="o">)</span><span class="n">view</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="监控usb事件">监控USB事件</h3>

<ol>
  <li>
    <p>初始化USBMonitor实例。</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">USBMonitor</span> <span class="n">mUSBMonitor</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="n">mUSBMonitor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">USBMonitor</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">mOnDeviceConnectListener</span><span class="o">);</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span> <span class="n">mOnDeviceConnectListener</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onAttach</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">Toast</span><span class="o">.</span><span class="na">makeText</span><span class="o">(</span><span class="nc">MainActivity</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="s">"USB_DEVICE_ATTACHED"</span><span class="o">,</span> <span class="nc">Toast</span><span class="o">.</span><span class="na">LENGTH_SHORT</span><span class="o">).</span><span class="na">show</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onConnect</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">UsbControlBlock</span> <span class="n">ctrlBlock</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">createNew</span><span class="o">)</span> <span class="o">{</span>

    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onDisconnect</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">UsbControlBlock</span> <span class="n">ctrlBlock</span><span class="o">)</span> <span class="o">{</span>

    <span class="o">}</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onDettach</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">Toast</span><span class="o">.</span><span class="na">makeText</span><span class="o">(</span><span class="nc">MainActivity</span><span class="o">.</span><span class="na">this</span><span class="o">,</span> <span class="s">"USB_DEVICE_DETACHED"</span><span class="o">,</span> <span class="nc">Toast</span><span class="o">.</span><span class="na">LENGTH_SHORT</span><span class="o">).</span><span class="na">show</span><span class="o">();</span>
    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onCancel</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">)</span> <span class="o">{</span>
    <span class="o">}</span>
<span class="o">};</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>管理生命周期。</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onStart</span><span class="o">()</span> <span class="o">{</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">onStart</span><span class="o">();</span>
    <span class="n">mUSBMonitor</span><span class="o">.</span><span class="na">register</span><span class="o">();</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onStop</span><span class="o">()</span> <span class="o">{</span>
    <span class="n">mUSBMonitor</span><span class="o">.</span><span class="na">unregister</span><span class="o">();</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">onStop</span><span class="o">();</span>
<span class="o">}</span>

<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">onDestroy</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">mUSBMonitor</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">mUSBMonitor</span><span class="o">.</span><span class="na">destroy</span><span class="o">();</span>
        <span class="n">mUSBMonitor</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="kd">super</span><span class="o">.</span><span class="na">onDestroy</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="启动摄像头">启动摄像头</h3>

<p><code class="language-plaintext highlighter-rouge">UVCCameraHandler</code>类用于处理USB摄像头。</p>

<ol>
  <li>
    <p>初始化。</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * set true if you want to record movie using MediaSurfaceEncoder
 * (writing frame data into Surface camera from MediaCodec
 *  by almost same way as USBCameratest2)
 * set false if you want to record movie using MediaVideoEncoder
 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="no">USE_SURFACE_ENCODER</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
<span class="cm">/**
 * preview resolution(width)
 * if your camera does not support specific resolution and mode,
 * {@link UVCCamera#setPreviewSize(int, int, int)} throw exception
 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">PREVIEW_WIDTH</span> <span class="o">=</span> <span class="mi">640</span><span class="o">;</span> <span class="c1">// 640</span>
<span class="cm">/**
 * preview resolution(height)
 * if your camera does not support specific resolution and mode,
 * {@link UVCCamera#setPreviewSize(int, int, int)} throw exception
 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">PREVIEW_HEIGHT</span> <span class="o">=</span> <span class="mi">480</span><span class="o">;</span> <span class="c1">//480</span>
<span class="cm">/**
 * preview mode
 * if your camera does not support specific resolution and mode,
 * {@link UVCCamera#setPreviewSize(int, int, int)} throw exception
 * 0:YUYV, other:MJPEG
 */</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">PREVIEW_MODE</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="c1">// YUV</span>
<span class="kd">private</span> <span class="nc">UVCCameraHandler</span> <span class="n">mCameraHandler</span><span class="o">;</span>
 <span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="n">mCameraHandler</span> <span class="o">=</span> <span class="nc">UVCCameraHandler</span><span class="o">.</span><span class="na">createHandler</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">mUVCCameraView</span><span class="o">,</span>
            <span class="no">USE_SURFACE_ENCODER</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="mi">1</span><span class="o">,</span> <span class="no">PREVIEW_WIDTH</span><span class="o">,</span> <span class="no">PREVIEW_HEIGHT</span><span class="o">,</span> <span class="no">PREVIEW_MODE</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>为图像按钮添加<code class="language-plaintext highlighter-rouge">OnClickListener</code>事件以启动摄像头选择对话框。选择摄像头后将开始视频流预览。</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="n">mCameraButton</span><span class="o">.</span><span class="na">setOnClickListener</span><span class="o">(</span><span class="n">mOnClickListener</span><span class="o">);</span>
    <span class="c1">//......</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kd">final</span> <span class="nc">View</span><span class="o">.</span><span class="na">OnClickListener</span> <span class="n">mOnClickListener</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">View</span><span class="o">.</span><span class="na">OnClickListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onClick</span><span class="o">(</span><span class="kd">final</span> <span class="nc">View</span> <span class="n">view</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">((</span><span class="n">mCameraHandler</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">mCameraHandler</span><span class="o">.</span><span class="na">isOpened</span><span class="o">())</span> <span class="o">{</span>
            <span class="nc">CameraDialog</span><span class="o">.</span><span class="na">showDialog</span><span class="o">(</span><span class="nc">MainActivity</span><span class="o">.</span><span class="na">this</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">mCameraHandler</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">};</span>

<span class="kd">private</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span> <span class="n">mOnDeviceConnectListener</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onConnect</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">UsbControlBlock</span> <span class="n">ctrlBlock</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">createNew</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">mCameraHandler</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">mCameraHandler</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="n">ctrlBlock</span><span class="o">);</span>
            <span class="n">startPreview</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">startPreview</span><span class="o">()</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">mCameraHandler</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="kd">final</span> <span class="nc">SurfaceTexture</span> <span class="n">st</span> <span class="o">=</span> <span class="n">mUVCCameraView</span><span class="o">.</span><span class="na">getSurfaceTexture</span><span class="o">();</span>
        <span class="n">mCameraHandler</span><span class="o">.</span><span class="na">startPreview</span><span class="o">(</span><span class="k">new</span> <span class="nc">Surface</span><span class="o">(</span><span class="n">st</span><span class="o">));</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>    </div>

    <p>还需要修改Activity以实现UVCCamera库的<code class="language-plaintext highlighter-rouge">CameraDialogParent</code>接口：</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MainActivity</span> <span class="kd">extends</span> <span class="nc">BaseActivity</span> <span class="kd">implements</span> <span class="nc">CameraDialog</span><span class="o">.</span><span class="na">CameraDialogParent</span>
</code></pre></div>    </div>

    <p>预览填充屏幕时画面会变形。可以使用以下代码保持其纵横比：</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mUVCCameraView</span><span class="o">.</span><span class="na">setAspectRatio</span><span class="o">(</span><span class="no">PREVIEW_WIDTH</span> <span class="o">/</span> <span class="o">(</span><span class="kt">double</span><span class="o">)</span><span class="no">PREVIEW_HEIGHT</span><span class="o">);</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="从usb摄像头预览读取mrz">从USB摄像头预览读取MRZ</h2>

<p>现在我们可以显示摄像头预览了，接着使用Dynamsoft Label Recognizer识别MRZ。</p>

<h3 id="初始化sdk">初始化SDK</h3>

<p>使用许可证初始化SDK。可以在<a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&amp;package=cross-platform">此处</a>申请一个30天试用许可证。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">CaptureVisionRouter</span> <span class="n">mRouter</span><span class="o">;</span> <span class="c1">//used to call Dynamsoft Label Recognizer to recognize MRZ</span>
<span class="kd">private</span> <span class="nc">CodeParser</span> <span class="n">mParser</span><span class="o">;</span> <span class="c1">//used to parse the recognized MRZ</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="nc">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">savedInstanceState</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
        <span class="nc">LicenseManager</span><span class="o">.</span><span class="na">initLicense</span><span class="o">(</span><span class="s">"LICENSE-KEY"</span><span class="o">,</span> <span class="k">this</span><span class="o">,</span> <span class="o">(</span><span class="n">isSuccess</span><span class="o">,</span> <span class="n">error</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(!</span><span class="n">isSuccess</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">error</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
            <span class="o">}</span>
        <span class="o">});</span>
    <span class="o">}</span>
    <span class="n">mRouter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CaptureVisionRouter</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
    <span class="n">mParser</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">CodeParser</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>

<h3 id="更新设置以识别mrz">更新设置以识别MRZ。</h3>

<p>我们需要使用以下JSON模板更新默认设置以支持识别MRZ。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">initMRZSettings</span><span class="o">(){</span>
    <span class="nc">String</span> <span class="n">template</span> <span class="o">=</span> <span class="s">"""
      {
          "</span><span class="nc">CaptureVisionTemplates</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">Default</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ReadBarcodes_Default</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_read_barcodes</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 200,
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">RecognizeTextLines_Default</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_number_letter</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">DetectDocumentBoundaries_Default</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_detect_document_boundaries</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">DetectAndNormalizeDocument_Default</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_detect_and_normalize_document</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">NormalizeDocument_Default</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_normalize_document</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ReadBarcodes_SpeedFirst</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_read_barcodes_speed_first</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 500
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ReadBarcodes_ReadRateFirst</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_read_barcodes_read_rate</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 5000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">ReadSingleBarcode</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_read_single_barcode</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 200,
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">RecognizeNumbers</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_number</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">RecognizeLetters</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_letter</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">RecognizeUppercaseLetters</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_uppercase</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">RecognizeNumbersAndUppercaseLetters</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_number_uppercase</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">RecognizeNumbersAndLetters</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_recognize_number_letter</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">":0
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">ReadPassportAndId</span><span class="s">",
                  "</span><span class="nc">OutputOriginalImage</span><span class="s">": 0,
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                        "</span><span class="n">roi_passport_and_id</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 2000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">ReadPassport</span><span class="s">",
                  "</span><span class="nc">OutputOriginalImage</span><span class="s">": 0,
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                        "</span><span class="n">roi_passport</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 2000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">ReadId</span><span class="s">",
                  "</span><span class="nc">OutputOriginalImage</span><span class="s">": 0,
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                        "</span><span class="n">roi_id</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 2000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">ReadDriversLicense</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">roi_pdf_417</span><span class="s">"
                  ],
                  "</span><span class="nc">SemanticProcessingNameArray</span><span class="s">": [ "</span><span class="n">sp_pdf_417</span><span class="s">" ],
                  "</span><span class="nc">Timeout</span><span class="s">" : 10000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="nc">ReadVIN</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">roi_vin_barcode</span><span class="s">",
                      "</span><span class="n">roi_vin_text</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">": 4,
                  "</span><span class="nc">SemanticProcessingNameArray</span><span class="s">": [
                      "</span><span class="n">sp_vin_barcode</span><span class="s">",
                      "</span><span class="n">sp_vin_text</span><span class="s">"
                  ],
                  "</span><span class="nc">Timeout</span><span class="s">": 10000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">ReadVINBarcode</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">roi_vin_barcode</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">" : 4,
                  "</span><span class="nc">SemanticProcessingNameArray</span><span class="s">": [ "</span><span class="n">sp_vin_barcode</span><span class="s">" ],
                  "</span><span class="nc">Timeout</span><span class="s">" : 10000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">ReadVINText</span><span class="s">",
                  "</span><span class="nc">ImageROIProcessingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">roi_vin_text</span><span class="s">"
                  ],
                  "</span><span class="nc">MaxParallelTasks</span><span class="s">" : 4,
                  "</span><span class="nc">SemanticProcessingNameArray</span><span class="s">": [ "</span><span class="n">sp_vin_text</span><span class="s">" ],
                  "</span><span class="nc">Timeout</span><span class="s">" : 10000
              }
          ],
          "</span><span class="nc">TargetROIDefOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_read_barcodes</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_read_barcodes</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_detect_document_boundaries</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_detect_document_boundaries</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_detect_and_normalize_document</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_detect_and_normalize_document</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_normalize_document</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_normalize_document</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_read_barcodes_speed_first</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_read_barcodes_speed_first</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_read_barcodes_read_rate</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_read_barcodes_read_rate</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_read_single_barcode</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_read_single_barcode</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_recognize_number</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_recognize_number</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_recognize_letter</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_recognize_letter</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_recognize_uppercase</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_recognize_uppercase</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_recognize_number_uppercase</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_recognize_number_uppercase</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_recognize_number_letter</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_recognize_number_letter</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_passport_and_id</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                        "</span><span class="n">task_passport_and_id</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_passport</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                        "</span><span class="n">task_passport</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">roi_id</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">task_id</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">roi_pdf_417</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">task_pdf_417</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">roi_vin_barcode</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">task_vin_barcode</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">roi_vin_text</span><span class="s">",
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">" :
                  [
                      "</span><span class="n">task_vin_text</span><span class="s">"
                  ]
              }
          ],
          "</span><span class="nc">BarcodeFormatSpecificationOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs1</span><span class="s">",
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">": [
                      "</span><span class="no">BF_PDF417</span><span class="s">",
                      "</span><span class="no">BF_QR_CODE</span><span class="s">",
                      "</span><span class="no">BF_DATAMATRIX</span><span class="s">",
                      "</span><span class="no">BF_AZTEC</span><span class="s">",
                      "</span><span class="no">BF_MICRO_QR</span><span class="s">",
                      "</span><span class="no">BF_MICRO_PDF417</span><span class="s">",
                      "</span><span class="no">BF_DOTCODE</span><span class="s">"
                  ],
                  "</span><span class="nc">MirrorMode</span><span class="s">": "</span><span class="no">MM_BOTH</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs2</span><span class="s">",
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">": [
                      "</span><span class="no">BF_ALL</span><span class="s">"
                  ],
                  "</span><span class="nc">MirrorMode</span><span class="s">": "</span><span class="no">MM_NORMAL</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs1_speed_first</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs1</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs2_speed_first</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs2</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs1_read_rate_first</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs1</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs2_read_rate_first</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs2</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs1_single_barcode</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs1</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">bfs2_single_barcode</span><span class="s">",
                  "</span><span class="nc">BaseBarcodeFormatSpecification</span><span class="s">": "</span><span class="n">bfs2</span><span class="s">"
              },
              {
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" :
                  [
                      "</span><span class="no">BF_CODE_39_EXTENDED</span><span class="s">"
                  ],
                  "</span><span class="nc">BarcodeTextRegExPattern</span><span class="s">" : "</span><span class="o">(</span><span class="no">I</span><span class="o">{</span><span class="mi">0</span><span class="o">,</span><span class="mi">1</span><span class="o">})([</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">TV</span><span class="o">-</span><span class="no">Y</span><span class="o">][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">5</span><span class="o">})</span><span class="s">",
                  "</span><span class="nc">MirrorMode</span><span class="s">" : "</span><span class="no">MM_NORMAL</span><span class="s">",
                  "</span><span class="nc">ModuleSizeRangeArray</span><span class="s">" : null,
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="no">VIN_CODE_39_EXTENDED</span><span class="s">"
              },
              {
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" :
                  [
                      "</span><span class="no">BF_DATAMATRIX</span><span class="s">"
                  ],
                  "</span><span class="nc">BarcodeTextRegExPattern</span><span class="s">" : "</span><span class="o">[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">TV</span><span class="o">-</span><span class="no">Y</span><span class="o">][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">5</span><span class="o">}</span><span class="s">",
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="no">VIN_DATAMATRIX</span><span class="s">"

              },
              {
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" :
                  [
                      "</span><span class="no">BF_QR_CODE</span><span class="s">"
                  ],
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="no">VIN_QR_CODE</span><span class="s">"
              }
          ],
          "</span><span class="nc">BarcodeReaderTaskSettingOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_read_barcodes</span><span class="s">",
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">" : 1,
                  "</span><span class="nc">BaseBarcodeReaderTaskSettingName</span><span class="s">": "</span><span class="n">task_read_single_barcode</span><span class="s">",
                  "</span><span class="nc">BarcodeFormatSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">bfs1</span><span class="s">",
                      "</span><span class="n">bfs2</span><span class="s">"
                  ],
                  "</span><span class="nc">LocalizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">LM_LINES</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_read_barcodes_speed_first</span><span class="s">",
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">" : 0,
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" : [ "</span><span class="no">BF_DEFAULT</span><span class="s">" ],
                  "</span><span class="nc">LocalizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">DeblurModes</span><span class="s">":[
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_BASED_ON_LOC_BIN</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_THRESHOLD_BINARIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_DEEP_ANALYSIS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BarcodeFormatSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">bfs1_speed_first</span><span class="s">",
                      "</span><span class="n">bfs2_speed_first</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_speed_first</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_speed_first</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_speed_first</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_read_barcodes_read_rate</span><span class="s">",
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">" : 999,
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" : [ "</span><span class="no">BF_DEFAULT</span><span class="s">" ],
                  "</span><span class="nc">LocalizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                       },
                       {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_LINES</span><span class="s">"
                       },
                       {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_STATISTICS</span><span class="s">"
                       }
                  ],
                  "</span><span class="nc">DeblurModes</span><span class="s">":[
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_BASED_ON_LOC_BIN</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_THRESHOLD_BINARIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_DIRECT_BINARIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_SMOOTHING</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_DEEP_ANALYSIS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BarcodeFormatSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">bfs1_read_rate_first</span><span class="s">",
                      "</span><span class="n">bfs2_read_rate_first</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_read_rate</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_read_rate</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_barcodes_read_rate</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_read_single_barcode</span><span class="s">",
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">":1,
                  "</span><span class="nc">LocalizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">LM_SCAN_DIRECTLY</span><span class="s">",
                          "</span><span class="nc">ScanDirection</span><span class="s">": 2
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">DeblurModes</span><span class="s">":[
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_BASED_ON_LOC_BIN</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_THRESHOLD_BINARIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">DM_DEEP_ANALYSIS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BarcodeFormatSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">bfs1_single_barcode</span><span class="s">",
                      "</span><span class="n">bfs2_single_barcode</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_single_barcode</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_single_barcode</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_read_single_barcode</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">task_pdf_417</span><span class="s">",
                  "</span><span class="nc">BarcodeColourModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">LightReflection</span><span class="s">" : 1,
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BICM_DARK_ON_LIGHT</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" :
                  [
                      "</span><span class="no">BF_PDF417</span><span class="s">"
                  ],
                  "</span><span class="nc">DeblurModes</span><span class="s">" : null,
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">" : 1,
                  "</span><span class="nc">LocalizationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_LINES</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_STATISTICS</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_localize_pdf417</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_localize_pdf417</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_decode_pdf417</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">task_vin_barcode</span><span class="s">",
                  "</span><span class="nc">DeblurModes</span><span class="s">" : null,
                  "</span><span class="nc">BarcodeColourModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">LightReflection</span><span class="s">" : 1,
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BICM_DARK_ON_LIGHT</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BarcodeFormatIds</span><span class="s">" :
                  [
                      "</span><span class="no">BF_CODE_39_EXTENDED</span><span class="s">",
                      "</span><span class="no">BF_QR_CODE</span><span class="s">",
                      "</span><span class="no">BF_DATAMATRIX</span><span class="s">"
                  ],
                  "</span><span class="nc">BarcodeFormatSpecificationNameArray</span><span class="s">" :
                  [
                      "</span><span class="no">VIN_CODE_39_EXTENDED</span><span class="s">",
                      "</span><span class="no">VIN_QR_CODE</span><span class="s">",
                      "</span><span class="no">VIN_DATAMATRIX</span><span class="s">"
                  ],
                  "</span><span class="nc">DeformationResistingModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">BinarizationMode</span><span class="s">" :
                          {
                              "</span><span class="nc">BinarizationThreshold</span><span class="s">" : -1,
                              "</span><span class="nc">BlockSizeX</span><span class="s">" : 0,
                              "</span><span class="nc">BlockSizeY</span><span class="s">" : 0,
                              "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">" : 0,
                              "</span><span class="nc">GrayscaleEnhancementModesIndex</span><span class="s">" : -1,
                              "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                              "</span><span class="nc">MorphOperation</span><span class="s">" : "</span><span class="nc">Close</span><span class="s">",
                              "</span><span class="nc">MorphOperationKernelSizeX</span><span class="s">" : -1,
                              "</span><span class="nc">MorphOperationKernelSizeY</span><span class="s">" : -1,
                              "</span><span class="nc">MorphShape</span><span class="s">" : "</span><span class="nc">Rectangle</span><span class="s">",
                              "</span><span class="nc">ThresholdCompensation</span><span class="s">" : 10
                          },
                          "</span><span class="nc">GrayscaleEnhancementMode</span><span class="s">" :
                          {
                              "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GEM_GENERAL</span><span class="s">",
                              "</span><span class="nc">Sensitivity</span><span class="s">" : -1,
                              "</span><span class="nc">SharpenBlockSizeX</span><span class="s">" : -1,
                              "</span><span class="nc">SharpenBlockSizeY</span><span class="s">" : -1,
                              "</span><span class="nc">SmoothBlockSizeX</span><span class="s">" : -1,
                              "</span><span class="nc">SmoothBlockSizeY</span><span class="s">" : -1
                          },
                          "</span><span class="nc">Level</span><span class="s">" : 5,
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">DRM_SKIP</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">ExpectedBarcodesCount</span><span class="s">" : 1,
                  "</span><span class="nc">LocalizationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_CONNECTED_BLOCKS</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">LM_SCAN_DIRECTLY</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_localize_vin</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_localize_vin</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_BARCODE_LOCALIZATION</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">ContinueWhenPartialResultsGenerated</span><span class="s">" : 1,
                          "</span><span class="nc">ImageParameterName</span><span class="s">" : "</span><span class="n">ip_decode_vin</span><span class="s">",
                          "</span><span class="nc">Section</span><span class="s">" : "</span><span class="no">ST_BARCODE_DECODING</span><span class="s">"
                      }
                  ]
              }
          ],
          "</span><span class="nc">TextLineSpecificationOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_number_letter</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">" : "</span><span class="nc">NumberLetter</span><span class="s">",
                  "</span><span class="nc">OutputResults</span><span class="s">": 1
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_number</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">" : "</span><span class="nc">Number</span><span class="s">",
                  "</span><span class="nc">OutputResults</span><span class="s">": 1
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_letter</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">" : "</span><span class="nc">Letter</span><span class="s">",
                  "</span><span class="nc">OutputResults</span><span class="s">": 1
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_uppercase</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">" : "</span><span class="nc">Uppercase</span><span class="s">",
                  "</span><span class="nc">OutputResults</span><span class="s">": 1
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_number_uppercase</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">" : "</span><span class="nc">NumberUppercase</span><span class="s">",
                  "</span><span class="nc">OutputResults</span><span class="s">": 1
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_mrz_passport</span><span class="s">",
                  "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">",
                  "</span><span class="nc">StringLengthRange</span><span class="s">": [ 44, 44 ],
                  "</span><span class="nc">OutputResults</span><span class="s">": 1,
                  "</span><span class="nc">ExpectedGroupsCount</span><span class="s">": 1,
                  "</span><span class="nc">ConcatResults</span><span class="s">": 1,
                  "</span><span class="nc">ConcatSeparator</span><span class="s">": "</span><span class="err">\\</span><span class="n">n</span><span class="s">",
                  "</span><span class="nc">SubGroups</span><span class="s">": [
                    {
                      "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">(</span><span class="no">P</span><span class="o">[</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">39</span><span class="o">}){(</span><span class="mi">44</span><span class="o">)}</span><span class="s">",
                      "</span><span class="nc">StringLengthRange</span><span class="s">": [ 44, 44 ],
                      "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                    },
                    {
                      "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">MF</span><span class="o">&lt;][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">14</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]){(</span><span class="mi">44</span><span class="o">)}</span><span class="s">",
                      "</span><span class="nc">StringLengthRange</span><span class="s">": [ 44, 44 ],
                      "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                    }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_mrz_id_td2</span><span class="s">",
                  "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">",
                  "</span><span class="nc">StringLengthRange</span><span class="s">": [ 36, 36 ],
                  "</span><span class="nc">OutputResults</span><span class="s">": 1,
                  "</span><span class="nc">ExpectedGroupsCount</span><span class="s">": 1,
                  "</span><span class="nc">ConcatResults</span><span class="s">": 1,
                  "</span><span class="nc">ConcatSeparator</span><span class="s">": "</span><span class="err">\\</span><span class="n">n</span><span class="s">",
                  "</span><span class="nc">SubGroups</span><span class="s">": [
                      {
                          "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="no">ACI</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">31</span><span class="o">}){(</span><span class="mi">36</span><span class="o">)}</span><span class="s">",
                          "</span><span class="nc">StringLengthRange</span><span class="s">": [ 36, 36 ],
                          "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">MF</span><span class="o">&lt;][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">8</span><span class="o">}){(</span><span class="mi">36</span><span class="o">)}</span><span class="s">",
                          "</span><span class="nc">StringLengthRange</span><span class="s">": [ 36, 36 ],
                          "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_mrz_id_td1</span><span class="s">",
                  "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">",
                  "</span><span class="nc">StringLengthRange</span><span class="s">": [ 30, 30 ],
                  "</span><span class="nc">OutputResults</span><span class="s">": 1,
                  "</span><span class="nc">ExpectedGroupsCount</span><span class="s">": 1,
                  "</span><span class="nc">ConcatResults</span><span class="s">": 1,
                  "</span><span class="nc">ConcatSeparator</span><span class="s">": "</span><span class="err">\\</span><span class="n">n</span><span class="s">",
                  "</span><span class="nc">SubGroups</span><span class="s">": [
                      {
                          "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="no">ACI</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;][</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">15</span><span class="o">}){(</span><span class="mi">30</span><span class="o">)}</span><span class="s">",
                          "</span><span class="nc">StringLengthRange</span><span class="s">": [ 30, 30 ],
                          "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">MF</span><span class="o">&lt;][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[(</span><span class="mo">01</span><span class="o">-</span><span class="mi">12</span><span class="o">)][(</span><span class="mo">01</span><span class="o">-</span><span class="mi">31</span><span class="o">)][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">][</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">3</span><span class="o">}[</span><span class="no">A</span><span class="o">-</span><span class="no">Z0</span><span class="o">-</span><span class="mi">9</span><span class="o">&lt;]{</span><span class="mi">11</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]){(</span><span class="mi">30</span><span class="o">)}</span><span class="s">",
                          "</span><span class="nc">StringLengthRange</span><span class="s">": [ 30, 30 ],
                          "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">([</span><span class="no">A</span><span class="o">-</span><span class="no">Z</span><span class="o">&lt;]{</span><span class="mi">30</span><span class="o">}){(</span><span class="mi">30</span><span class="o">)}</span><span class="s">",
                          "</span><span class="nc">StringLengthRange</span><span class="s">": [ 30, 30 ],
                          "</span><span class="nc">BaseTextLineSpecificationName</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_base</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">": "</span><span class="no">MRZ</span><span class="s">",
                  "</span><span class="nc">CharHeightRange</span><span class="s">": [ 5, 1000, 1 ],
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 30,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 30,
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0,
                          "</span><span class="nc">ThresholdCompensation</span><span class="s">": 15
                      }
                  ],
                  "</span><span class="nc">ConfusableCharactersCorrection</span><span class="s">": {
                      "</span><span class="nc">ConfusableCharacters</span><span class="s">": [
                          [ "</span><span class="mi">0</span><span class="s">", "</span><span class="no">O</span><span class="s">" ],
                          [ "</span><span class="mi">1</span><span class="s">", "</span><span class="no">I</span><span class="s">" ],
                          [ "</span><span class="mi">5</span><span class="s">", "</span><span class="no">S</span><span class="s">" ]
                      ],
                      "</span><span class="nc">FontNameArray</span><span class="s">": [ "</span><span class="no">OCR_B</span><span class="s">" ]
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">tls_vin_text</span><span class="s">",
                  "</span><span class="nc">CharacterModelName</span><span class="s">": "</span><span class="no">VIN</span><span class="s">",
                  "</span><span class="nc">StringRegExPattern</span><span class="s">": "</span><span class="o">[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">9</span><span class="o">}[</span><span class="mi">1</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">TV</span><span class="o">-</span><span class="no">Y</span><span class="o">][</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="no">A</span><span class="o">-</span><span class="no">HJ</span><span class="o">-</span><span class="no">NPR</span><span class="o">-</span><span class="no">Z</span><span class="o">]{</span><span class="mi">2</span><span class="o">}[</span><span class="mi">0</span><span class="o">-</span><span class="mi">9</span><span class="o">]{</span><span class="mi">5</span><span class="o">}</span><span class="s">",
                  "</span><span class="nc">CharHeightRange</span><span class="s">": [5, 1000, 1],
                  "</span><span class="nc">StringLengthRange</span><span class="s">": [17, 17]
              }
          ],
          "</span><span class="nc">LabelRecognizerTaskSettingOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_recognize_number_letter</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_number_letter</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_letter</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_letter</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_letter</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_recognize_number</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_number</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_textline_number</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_textline_number</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_textline_number</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_recognize_letter</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_letter</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_letter</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_letter</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_letter</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_recognize_uppercase</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_uppercase</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_uppercase</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_uppercase</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_uppercase</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_recognize_number_uppercase</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_number_uppercase</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_uppercase</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_uppercase</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_number_uppercase</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_passport</span><span class="s">",
                  "</span><span class="nc">ConfusableCharactersPath</span><span class="s">": "</span><span class="nc">ConfusableChars</span><span class="o">.</span><span class="na">data</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": ["</span><span class="n">tls_mrz_passport</span><span class="s">"],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_id</span><span class="s">",
                  "</span><span class="nc">ConfusableCharactersPath</span><span class="s">": "</span><span class="nc">ConfusableChars</span><span class="o">.</span><span class="na">data</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": ["</span><span class="n">tls_mrz_id_td1</span><span class="s">", "</span><span class="n">tls_mrz_id_td2</span><span class="s">"],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_passport_and_id</span><span class="s">",
                  "</span><span class="nc">ConfusableCharactersPath</span><span class="s">": "</span><span class="nc">ConfusableChars</span><span class="o">.</span><span class="na">data</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": ["</span><span class="n">tls_mrz_passport</span><span class="s">", "</span><span class="n">tls_mrz_id_td1</span><span class="s">", "</span><span class="n">tls_mrz_id_td2</span><span class="s">"],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_vin_text</span><span class="s">",
                  "</span><span class="nc">TextLineSpecificationNameArray</span><span class="s">": [
                      "</span><span class="n">tls_vin_text</span><span class="s">"
                  ],
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_vin</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_LOCALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_vin</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_TEXT_LINE_RECOGNITION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_recognize_vin</span><span class="s">"
                      }
                  ]
              }
          ],
          "</span><span class="nc">DocumentNormalizerTaskSettingOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_detect_and_normalize_document</span><span class="s">",
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect_and_normalize</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_DETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect_and_normalize</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_NORMALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect_and_normalize</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_detect_document_boundaries</span><span class="s">",
                  "</span><span class="nc">TerminateSetting</span><span class="s">": {
                      "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_DETECTION</span><span class="s">"
                  },
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_DETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_NORMALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_detect</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">task_normalize_document</span><span class="s">",
                  "</span><span class="nc">StartSection</span><span class="s">": "</span><span class="no">ST_DOCUMENT_NORMALIZATION</span><span class="s">",
                  "</span><span class="nc">SectionImageParameterArray</span><span class="s">": [
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_REGION_PREDETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_normalize</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_DETECTION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_normalize</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Section</span><span class="s">": "</span><span class="no">ST_DOCUMENT_NORMALIZATION</span><span class="s">",
                          "</span><span class="nc">ImageParameterName</span><span class="s">": "</span><span class="n">ip_normalize</span><span class="s">"
                      }
                  ]
              }
          ],
          "</span><span class="nc">ImageParameterOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_read_barcodes</span><span class="s">",
                  "</span><span class="nc">BaseImageParameterName</span><span class="s">": "</span><span class="n">ip_read_single_barcode</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">ip_localize_pdf417</span><span class="s">",
                  "</span><span class="nc">BinarizationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">GrayscaleEnhancementModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GEM_GENERAL</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">ip_decode_pdf417</span><span class="s">",
                  "</span><span class="nc">ScaleDownThreshold</span><span class="s">" : 99999
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_read_barcodes_speed_first</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">UNKNOWN</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 3
                  },
                  "</span><span class="nc">IfEraseTextZone</span><span class="s">": 1,
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 0,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 0,
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_read_barcodes_read_rate</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">UNKNOWN</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 3
                  },
                  "</span><span class="nc">IfEraseTextZone</span><span class="s">": 1,
                  "</span><span class="nc">GrayscaleTransformationModes</span><span class="s">" : [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">GTM_ORIGINAL</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">GTM_INVERTED</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">ScaleDownThreshold</span><span class="s">" : 100000
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_read_single_barcode</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">UNKNOWN</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 3
                  },
                  "</span><span class="nc">IfEraseTextZone</span><span class="s">": 1,
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 39,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 39,
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_number_letter</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          20,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_textline_number</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          20,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_letter</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          20,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_uppercase</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          20,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_number_uppercase</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          20,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_detect_and_normalize</span><span class="s">",
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 0,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 0,
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0
                      }
                  ],
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_WORD</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_detect</span><span class="s">",
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 0,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 0,
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0
                      }
                  ],
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_WORD</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_normalize</span><span class="s">",
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">BlockSizeX</span><span class="s">": 0,
                          "</span><span class="nc">BlockSizeY</span><span class="s">": 0,
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0
                      }
                  ],
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_WORD</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_mrz</span><span class="s">",
                  "</span><span class="nc">TextureDetectionModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TDM_GENERAL_WIDTH_CONCENTRATION</span><span class="s">",
                          "</span><span class="nc">Sensitivity</span><span class="s">": 8
                      }
                  ],
                  "</span><span class="nc">BinarizationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">": 0,
                          "</span><span class="nc">ThresholdCompensation</span><span class="s">": 21,
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [ 5, 1000, 1 ],
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  }
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">ip_localize_vin</span><span class="s">",
                  "</span><span class="nc">BinarizationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">",
                          "</span><span class="nc">MorphOperation</span><span class="s">" : "</span><span class="nc">Erode</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">GrayscaleEnhancementModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GEM_GENERAL</span><span class="s">",
                          "</span><span class="nc">Sensitivity</span><span class="s">" : 5,
                          "</span><span class="nc">SharpenBlockSizeX</span><span class="s">" : 3,
                          "</span><span class="nc">SharpenBlockSizeY</span><span class="s">" : 3,
                          "</span><span class="nc">SmoothBlockSizeX</span><span class="s">" : 3,
                          "</span><span class="nc">SmoothBlockSizeY</span><span class="s">" : 3
                      }
                  ],
                  "</span><span class="nc">GrayscaleTransformationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GTM_ORIGINAL</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GTM_INVERTED</span><span class="s">"
                      }
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="n">ip_decode_vin</span><span class="s">",
                  "</span><span class="nc">GrayscaleTransformationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">GTM_ORIGINAL</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">ScaleDownThreshold</span><span class="s">" : 99999
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">ip_recognize_vin</span><span class="s">",
                  "</span><span class="nc">TextDetectionMode</span><span class="s">": {
                      "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">TTDM_LINE</span><span class="s">",
                      "</span><span class="nc">Direction</span><span class="s">": "</span><span class="no">HORIZONTAL</span><span class="s">",
                      "</span><span class="nc">CharHeightRange</span><span class="s">": [
                          5,
                          1000,
                          1
                      ],
                      "</span><span class="nc">Sensitivity</span><span class="s">": 7
                  },
                  "</span><span class="nc">GrayscaleTransformationModes</span><span class="s">": [
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">GTM_ORIGINAL</span><span class="s">"
                      },
                      {
                          "</span><span class="nc">Mode</span><span class="s">": "</span><span class="no">GTM_INVERTED</span><span class="s">"
                      }
                  ],
                  "</span><span class="nc">BinarizationModes</span><span class="s">" :
                  [
                      {
                          "</span><span class="nc">EnableFillBinaryVacancy</span><span class="s">" : 1,
                          "</span><span class="nc">Mode</span><span class="s">" : "</span><span class="no">BM_LOCAL_BLOCK</span><span class="s">"
                      }
                  ]
              }
             \s
          ],
          "</span><span class="nc">CharacterModelOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">Number</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">Letter</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">Uppercase</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">NumberUppercase</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">" : "</span><span class="nc">NumberLetter</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="no">MRZ</span><span class="s">"
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="no">VIN</span><span class="s">"
              }
          ],
          "</span><span class="nc">SemanticProcessingOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_passport_and_id</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                      "</span><span class="n">roi_passport_and_id</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_passport_and_id</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_passport</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                      "</span><span class="n">roi_passport</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_passport</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_id</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                      "</span><span class="n">roi_id</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_id</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_pdf_417</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                              "</span><span class="n">roi_pdf_417</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_pdf_417</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_vin_barcode</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                              "</span><span class="n">roi_vin_barcode</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_vin_barcode</span><span class="s">"
                  ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">sp_vin_text</span><span class="s">",
                  "</span><span class="nc">ReferenceObjectFilter</span><span class="s">": {
                      "</span><span class="nc">ReferenceTargetROIDefNameArray</span><span class="s">": [
                          "</span><span class="n">roi_vin_text</span><span class="s">"
                      ]
                  },
                  "</span><span class="nc">TaskSettingNameArray</span><span class="s">": [
                      "</span><span class="n">parse_vin_text</span><span class="s">"
                  ]
              }
          ],
          "</span><span class="nc">CodeParserTaskSettingOptions</span><span class="s">": [
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_passport</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": [ "</span><span class="no">MRTD_TD3_PASSPORT</span><span class="s">" ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_id</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": [ "</span><span class="no">MRTD_TD1_ID</span><span class="s">", "</span><span class="no">MRTD_TD2_ID</span><span class="s">" ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_passport_and_id</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": [ "</span><span class="no">MRTD_TD3_PASSPORT</span><span class="s">", "</span><span class="no">MRTD_TD1_ID</span><span class="s">", "</span><span class="no">MRTD_TD2_ID</span><span class="s">" ]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_pdf_417</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": ["</span><span class="no">AAMVA_DL_ID</span><span class="s">","</span><span class="no">AAMVA_DL_ID_WITH_MAG_STRIPE</span><span class="s">","</span><span class="no">SOUTH_AFRICA_DL</span><span class="s">"]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_vin_barcode</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": ["</span><span class="no">VIN</span><span class="s">"]
              },
              {
                  "</span><span class="nc">Name</span><span class="s">": "</span><span class="n">parse_vin_text</span><span class="s">",
                  "</span><span class="nc">CodeSpecifications</span><span class="s">": ["</span><span class="no">VIN</span><span class="s">"]
              }
          ]
      }
      """</span><span class="o">;</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">mRouter</span><span class="o">.</span><span class="na">initSettings</span><span class="o">(</span><span class="n">template</span><span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">CaptureVisionRouterException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>我们还可以更改<code class="language-plaintext highlighter-rouge">PREVIEW_WIDTH</code>和<code class="language-plaintext highlighter-rouge">PREVIEW_HEIGHT</code>来更新要使用的USB 摄像头的分辨率。OCR通常要求较高的分辨率。</p>

<h3 id="创建计时器来处理帧">创建计时器来处理帧</h3>

<p>连接摄像头后，使用Timer计时器不断获取视频帧进行处理。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="nc">Timer</span> <span class="n">timer</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">startProcessing</span><span class="o">(){</span>
    <span class="nc">TimerTask</span> <span class="n">task</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TimerTask</span><span class="o">()</span> <span class="o">{</span>
        <span class="nd">@Override</span>
        <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
            <span class="nc">Bitmap</span> <span class="n">bmp</span> <span class="o">=</span> <span class="n">mUVCCameraView</span><span class="o">.</span><span class="na">captureStillImage</span><span class="o">();</span>
            <span class="n">recognize</span><span class="o">(</span><span class="n">bmp</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">};</span>
    <span class="n">timer</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Timer</span><span class="o">();</span>
    <span class="n">timer</span><span class="o">.</span><span class="na">scheduleAtFixedRate</span><span class="o">(</span><span class="n">task</span><span class="o">,</span> <span class="mi">1000</span><span class="o">,</span> <span class="mi">100</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">stopProcessing</span><span class="o">(){</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">timer</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">){</span>
        <span class="n">timer</span><span class="o">.</span><span class="na">cancel</span><span class="o">();</span>
        <span class="n">timer</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span> <span class="n">mOnDeviceConnectListener</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">OnDeviceConnectListener</span><span class="o">()</span> <span class="o">{</span>
    <span class="c1">//......</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onConnect</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">UsbControlBlock</span> <span class="n">ctrlBlock</span><span class="o">,</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">createNew</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">mCameraHandler</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">mCameraHandler</span><span class="o">.</span><span class="na">open</span><span class="o">(</span><span class="n">ctrlBlock</span><span class="o">);</span>
            <span class="n">startPreview</span><span class="o">();</span>
            <span class="n">startProcessing</span><span class="o">();</span> <span class="c1">//newly added</span>
        <span class="o">}</span>

    <span class="o">}</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onDisconnect</span><span class="o">(</span><span class="kd">final</span> <span class="nc">UsbDevice</span> <span class="n">device</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">USBMonitor</span><span class="o">.</span><span class="na">UsbControlBlock</span> <span class="n">ctrlBlock</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">stopProcessing</span><span class="o">();</span>  <span class="c1">//newly added</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>预览将捕获为bitmap，供Dynamsoft SDK处理。结果将显示在TextView中。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">recognize</span><span class="o">(</span><span class="kd">final</span> <span class="nc">Bitmap</span> <span class="n">bitmap</span><span class="o">){</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="nc">CapturedResult</span> <span class="n">result</span> <span class="o">=</span> <span class="n">mRouter</span><span class="o">.</span><span class="na">capture</span><span class="o">(</span><span class="n">bitmap</span><span class="o">,</span> <span class="s">"ReadPassportAndId"</span><span class="o">);</span> <span class="c1">//recognize MRZ</span>
        <span class="nc">RecognizedTextLinesResult</span> <span class="n">linesResult</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="na">getRecognizedTextLinesResult</span><span class="o">();</span>
        <span class="n">runOnUiThread</span><span class="o">(</span>
            <span class="k">new</span> <span class="nf">Runnable</span><span class="o">()</span> <span class="o">{</span>
                <span class="nd">@Override</span>
                <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
                    <span class="k">if</span> <span class="o">(</span><span class="n">linesResult</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                        <span class="nc">TextLineResultItem</span><span class="o">[]</span> <span class="n">results</span> <span class="o">=</span> <span class="n">linesResult</span><span class="o">.</span><span class="na">getItems</span><span class="o">();</span>
                        <span class="nc">StringBuilder</span> <span class="n">linesBuilder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">();</span>
                        <span class="k">for</span> <span class="o">(</span><span class="nc">TextLineResultItem</span> <span class="n">result</span> <span class="o">:</span> <span class="n">results</span><span class="o">){</span>
                            <span class="n">linesBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">getText</span><span class="o">());</span>
                            <span class="n">linesBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"\n"</span><span class="o">);</span>
                        <span class="o">}</span>
                        <span class="k">try</span> <span class="o">{</span>
                            <span class="nc">StringBuilder</span> <span class="n">result</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">();</span>
                            <span class="nc">ParsedResultItem</span> <span class="n">item</span> <span class="o">=</span> <span class="n">mParser</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">linesBuilder</span><span class="o">.</span><span class="na">toString</span><span class="o">().</span><span class="na">getBytes</span><span class="o">(</span><span class="nc">StandardCharsets</span><span class="o">.</span><span class="na">UTF_8</span><span class="o">),</span><span class="s">""</span><span class="o">);</span> <span class="c1">//parse MRZ</span>
                            <span class="k">for</span> <span class="o">(</span><span class="nc">HashMap</span><span class="o">.</span><span class="na">Entry</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">&gt;</span> <span class="n">entry</span> <span class="o">:</span> <span class="n">item</span><span class="o">.</span><span class="na">getParsedFields</span><span class="o">().</span><span class="na">entrySet</span><span class="o">())</span> <span class="o">{</span>
                                <span class="nc">String</span> <span class="n">key</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="na">getKey</span><span class="o">();</span>
                                <span class="nc">String</span> <span class="n">value</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="na">getValue</span><span class="o">();</span>
                                <span class="n">result</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">key</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="s">": "</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="n">value</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="s">"\n"</span><span class="o">);</span>
                            <span class="o">}</span>
                            <span class="n">resultTextView</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">result</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
                        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">CodeParserException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                            <span class="n">resultTextView</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">getMessage</span><span class="o">());</span>
                        <span class="o">}</span>
                    <span class="o">}</span>
                <span class="o">}</span>
            <span class="o">}</span>
        <span class="o">);</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">){</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="兼容问题">兼容问题</h2>

<p>在Android 9+上，如果<code class="language-plaintext highlighter-rouge">targetSdkVersion</code>大于27，则库可能无法工作。可以查看此<a href="https://serenegiant.com/blog/?p=3696">博客</a>以了解更多信息。</p>

<h2 id="源代码">源代码</h2>

<p><a href="https://github.com/tony-xlh/Android-USB-Camera-MRZ-Scanner/">https://github.com/tony-xlh/Android-USB-Camera-MRZ-Scanner/</a></p>]]></content><author><name>大名维森软件</name></author><category term="文字识别" /><summary type="html"><![CDATA[本文介绍了如何编写一个使用USB摄像头扫描护照MRZ的安卓应用。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Electron护照扫描桌面应用</title><link href="https://devblogs.damingsoft.com/electron-passport-scanner-desktop-app/" rel="alternate" type="text/html" title="Electron护照扫描桌面应用" /><published>2025-02-21T08:14:53+00:00</published><updated>2025-02-21T08:14:53+00:00</updated><id>https://devblogs.damingsoft.com/electron-passport-scanner-desktop-app</id><content type="html" xml:base="https://devblogs.damingsoft.com/electron-passport-scanner-desktop-app/"><![CDATA[<p>Electron是一个使用JavaScript、HTML和CSS构建桌面应用程序的框架。随着Web技术成为构建用户界面的最佳选择，越来越多的开发人员正在采用Electron来构建他们的应用程序。</p>

<p>在本文中，我们将使用Electron和<a href="https://www.dynamsoft.com/label-recognition/overview/">Dynamsoft Label Recognizer</a>创建一个护照扫描桌面应用程序来展示相关技术。</p>

<p>屏幕截图：</p>

<p><img src="/album/2025/02/electron/passport-scanner.jpg" alt="护照扫描应用截图" /></p>

<p>我们可以看到MRZ（机器可读区域）被识别了出来，并提取了所有者的信息，如姓名和国籍。</p>

<h2 id="新建项目">新建项目</h2>

<ol>
  <li>
    <p>创建一个新项目。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm init
</code></pre></div>    </div>
  </li>
  <li>
    <p>安装Electron。</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install </span>electron <span class="nt">--save-dev</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>创建<code class="language-plaintext highlighter-rouge">index.html</code>文件：</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
  <span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span> <span class="nt">/&gt;</span>
    <span class="c">&lt;!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --&gt;</span>
    <span class="nt">&lt;meta</span>
      <span class="na">http-equiv=</span><span class="s">"Content-Security-Policy"</span>
      <span class="na">content=</span><span class="s">"default-src 'self'; script-src 'self'"</span>
    <span class="nt">/&gt;</span>
    <span class="nt">&lt;meta</span>
      <span class="na">http-equiv=</span><span class="s">"X-Content-Security-Policy"</span>
      <span class="na">content=</span><span class="s">"default-src 'self'; script-src 'self'"</span>
    <span class="nt">/&gt;</span>
    <span class="nt">&lt;title&gt;</span>Passport Scanner<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;/head&gt;</span>
  <span class="nt">&lt;body&gt;</span>
    <span class="nt">&lt;h1&gt;</span>Passport Scanner<span class="nt">&lt;/h1&gt;</span>
  <span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>创建<code class="language-plaintext highlighter-rouge">main.js</code>文件作为项目的入口以用Electron启动。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">app</span><span class="p">,</span> <span class="nx">BrowserWindow</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">electron/main</span><span class="dl">'</span><span class="p">)</span>

<span class="kd">const</span> <span class="nx">createWindow</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">win</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span>
    <span class="na">width</span><span class="p">:</span> <span class="mi">800</span><span class="p">,</span>
    <span class="na">height</span><span class="p">:</span> <span class="mi">600</span>
  <span class="p">})</span>

  <span class="nx">win</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="dl">'</span><span class="s1">index.html</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">whenReady</span><span class="p">().</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="nx">createWindow</span><span class="p">()</span>

  <span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">activate</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">BrowserWindow</span><span class="p">.</span><span class="nx">getAllWindows</span><span class="p">().</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">createWindow</span><span class="p">()</span>
    <span class="p">}</span>
  <span class="p">})</span>
<span class="p">})</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">window-all-closed</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">platform</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">darwin</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">app</span><span class="p">.</span><span class="nx">quit</span><span class="p">()</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>运行<code class="language-plaintext highlighter-rouge">npx electron.</code>启动程序。</p>
  </li>
</ol>

<h2 id="添加依赖项">添加依赖项</h2>

<p>安装Dynamsoft的node包，以添加从护照图像中识别MRZ文本的功能。</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install </span>dynamsoft-capture-vision-for-node dynamsoft-capture-vision-for-node-charactermodel
</code></pre></div></div>

<h2 id="打开摄像头">打开摄像头</h2>

<p>接下来，使用<code class="language-plaintext highlighter-rouge">getUserMedia</code>打开连接的摄像头。</p>

<ol>
  <li>
    <p>在HTML文件中添加元素。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div&gt;</span>
  <span class="nt">&lt;label&gt;</span>
    Camera:
    <span class="nt">&lt;select</span> <span class="na">id=</span><span class="s">"select-camera"</span><span class="nt">&gt;&lt;/select&gt;</span>
  <span class="nt">&lt;/label&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"button-start"</span><span class="nt">&gt;</span>Start Camera<span class="nt">&lt;/button&gt;</span>
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"camera-container"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;video</span> <span class="na">id=</span><span class="s">"camera"</span> <span class="na">autoplay</span> <span class="na">playsinline</span><span class="nt">&gt;&lt;/video&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>请求摄像头权限。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">askForPermissions</span><span class="p">(){</span>
  <span class="kd">var</span> <span class="nx">stream</span><span class="p">;</span>
  <span class="k">try</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">constraints</span> <span class="o">=</span> <span class="p">{</span><span class="na">video</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">audio</span><span class="p">:</span> <span class="kc">false</span><span class="p">};</span> <span class="c1">//ask for camera permission</span>
    <span class="nx">stream</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">mediaDevices</span><span class="p">.</span><span class="nx">getUserMedia</span><span class="p">(</span><span class="nx">constraints</span><span class="p">);</span>  
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nx">closeStream</span><span class="p">(</span><span class="nx">stream</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">closeStream</span><span class="p">(</span><span class="nx">stream</span><span class="p">){</span>
  <span class="k">try</span><span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">stream</span><span class="p">){</span>
      <span class="nx">stream</span><span class="p">.</span><span class="nx">getTracks</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">track</span> <span class="o">=&gt;</span> <span class="nx">track</span><span class="p">.</span><span class="nx">stop</span><span class="p">());</span>
    <span class="p">}</span>
  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">){</span>
    <span class="nx">alert</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>列出摄像头设备。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">listDevices</span><span class="p">(){</span>
  <span class="nx">devices</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">getCameraDevices</span><span class="p">()</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">index</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">index</span> <span class="o">&lt;</span> <span class="nx">devices</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">index</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">device</span> <span class="o">=</span> <span class="nx">devices</span><span class="p">[</span><span class="nx">index</span><span class="p">];</span>
    <span class="nx">camSelect</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="k">new</span> <span class="nx">Option</span><span class="p">(</span><span class="nx">device</span><span class="p">.</span><span class="nx">label</span> <span class="o">??</span> <span class="dl">"</span><span class="s2">Camera </span><span class="dl">"</span><span class="o">+</span><span class="nx">index</span><span class="p">,</span><span class="nx">device</span><span class="p">.</span><span class="nx">deviceId</span><span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">async</span> <span class="kd">function</span> <span class="nx">getCameraDevices</span><span class="p">(){</span>
  <span class="k">await</span> <span class="nx">askForPermissions</span><span class="p">();</span>
  <span class="kd">var</span> <span class="nx">allDevices</span> <span class="o">=</span> <span class="k">await</span> <span class="nb">navigator</span><span class="p">.</span><span class="nx">mediaDevices</span><span class="p">.</span><span class="nx">enumerateDevices</span><span class="p">();</span>
  <span class="kd">var</span> <span class="nx">cameraDevices</span> <span class="o">=</span> <span class="p">[];</span>
  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="nx">i</span><span class="o">&lt;</span><span class="nx">allDevices</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span><span class="nx">i</span><span class="o">++</span><span class="p">){</span>
    <span class="kd">var</span> <span class="nx">device</span> <span class="o">=</span> <span class="nx">allDevices</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">device</span><span class="p">.</span><span class="nx">kind</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">videoinput</span><span class="dl">'</span><span class="p">){</span>
      <span class="nx">cameraDevices</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">device</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">cameraDevices</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>打开所选摄像头。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">startCamera</span><span class="p">(){</span>
  <span class="kd">var</span> <span class="nx">video</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">camera</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">selectedCamera</span> <span class="o">=</span> <span class="nx">camSelect</span><span class="p">.</span><span class="nx">selectedOptions</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">constraints</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">audio</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span>
    <span class="na">video</span><span class="p">:</span><span class="kc">true</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">selectedCamera</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">constraints</span> <span class="o">=</span> <span class="p">{</span>
      <span class="na">video</span><span class="p">:</span> <span class="p">{</span><span class="na">deviceId</span><span class="p">:</span> <span class="nx">selectedCamera</span><span class="p">},</span>
      <span class="na">audio</span><span class="p">:</span> <span class="kc">false</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="nb">navigator</span><span class="p">.</span><span class="nx">mediaDevices</span><span class="p">.</span><span class="nx">getUserMedia</span><span class="p">(</span><span class="nx">constraints</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">camera</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">video</span><span class="p">.</span><span class="nx">srcObject</span> <span class="o">=</span> <span class="nx">camera</span><span class="p">;</span>
  <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">alert</span><span class="p">(</span><span class="dl">'</span><span class="s1">Unable to capture your camera. Please check console logs.</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="捕获帧为dataurl">捕获帧为DataURL</h2>

<p>使用Canvas将帧捕获为DataURL，以便之后用于识别MRZ。</p>

<p>HTML：</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"result-container"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;canvas</span> <span class="na">id=</span><span class="s">"captured"</span><span class="nt">&gt;&lt;/canvas&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>JavaScript：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">capture</span><span class="p">(){</span>
  <span class="kd">var</span> <span class="nx">video</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">camera</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">captured</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">video</span><span class="p">.</span><span class="nx">videoWidth</span><span class="p">;</span>
  <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">video</span><span class="p">.</span><span class="nx">videoHeight</span><span class="p">;</span>
  <span class="nx">context</span><span class="p">.</span><span class="nx">drawImage</span><span class="p">(</span><span class="nx">video</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">dataurl</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">toDataURL</span><span class="p">(</span><span class="dl">"</span><span class="s2">image/jpeg</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="识别mrz">识别MRZ</h2>

<p>进程间通信（IPC）是在Electron中构建功能丰富的桌面应用程序的关键部分。在Electron中，进程通过开发者定义的“通道”与ipcMain和ipcRenderer模块传递信息进行通信。</p>

<p>Dynamsoft SDK的node版在main进程中运行。我们需要将DataURL从renderer传递到main，然后将识别的结果从main传递到renderer。</p>

<p>先在<code class="language-plaintext highlighter-rouge">main.js</code>中添加相关函数。</p>

<ol>
  <li>
    <p>初始化Dynamsoft SDK的许可证。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">initLicense</span><span class="p">(){</span>
    <span class="nx">LicenseManager</span><span class="p">.</span><span class="nx">initLicense</span><span class="p">(</span><span class="dl">'</span><span class="s1">LICENSE-KEY</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>更新运行时设置以支持识别MRZ。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">initSettings</span><span class="p">(){</span>
  <span class="kd">let</span> <span class="nx">mrzTemplate</span> <span class="o">=</span> <span class="s2">`{
  "CaptureVisionTemplates": [
    {
      "Name": "ReadPassportAndId",
      "OutputOriginalImage": 1,
      "ImageROIProcessingNameArray": ["roi-passport-and-id"],
      "SemanticProcessingNameArray": ["sp-passport-and-id"],
      "Timeout": 2000
    },
    {
      "Name": "ReadPassport",
      "OutputOriginalImage": 1,
      "ImageROIProcessingNameArray": ["roi-passport"],
      "SemanticProcessingNameArray": ["sp-passport"],
      "Timeout": 2000
    },
    {
      "Name": "ReadId",
      "OutputOriginalImage": 1,
      "ImageROIProcessingNameArray": ["roi-id"],
      "SemanticProcessingNameArray": ["sp-id"],
      "Timeout": 2000
    }
  ],
  "TargetROIDefOptions": [
    {
      "Name": "roi-passport-and-id",
      "TaskSettingNameArray": ["task-passport-and-id"]
    },
    {
      "Name": "roi-passport",
      "TaskSettingNameArray": ["task-passport"]
    },
    {
      "Name": "roi-id",
      "TaskSettingNameArray": ["task-id"]
    }
  ],
  "TextLineSpecificationOptions": [
    {
      "Name": "tls_mrz_passport",
      "BaseTextLineSpecificationName": "tls_base",
      "StringLengthRange": [44, 44],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\\n",
      "SubGroups": [
        {
          "StringRegExPattern": "(P[A-Z&lt;][A-Z&lt;]{3}[A-Z&lt;]{39}){(44)}",
          "StringLengthRange": [44, 44],
          "BaseTextLineSpecificationName": "tls_base"
        },
        {
          "StringRegExPattern": "([A-Z0-9&lt;]{9}[0-9][A-Z&lt;]{3}[0-9]{2}[0-9&lt;]{4}[0-9][MF&lt;][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9&lt;]{14}[0-9&lt;][0-9]){(44)}",
          "StringLengthRange": [44, 44],
          "BaseTextLineSpecificationName": "tls_base"
        }
      ]
    },
    {
      "Name": "tls_mrz_id_td2",
      "BaseTextLineSpecificationName": "tls_base",
      "StringLengthRange": [36, 36],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\\n",
      "SubGroups": [
        {
          "StringRegExPattern": "([ACI][A-Z&lt;][A-Z&lt;]{3}[A-Z&lt;]{31}){(36)}",
          "StringLengthRange": [36, 36],
          "BaseTextLineSpecificationName": "tls_base"
        },
        {
          "StringRegExPattern": "([A-Z0-9&lt;]{9}[0-9][A-Z&lt;]{3}[0-9]{2}[0-9&lt;]{4}[0-9][MF&lt;][0-9]{2}[(01-12)][(01-31)][0-9][A-Z0-9&lt;]{8}){(36)}",
          "StringLengthRange": [36, 36],
          "BaseTextLineSpecificationName": "tls_base"
        }
      ]
    },
    {
      "Name": "tls_mrz_id_td1",
      "BaseTextLineSpecificationName": "tls_base",
      "StringLengthRange": [30, 30],
      "OutputResults": 1,
      "ExpectedGroupsCount": 1,
      "ConcatResults": 1,
      "ConcatSeparator": "\\n",
      "SubGroups": [
        {
          "StringRegExPattern": "([ACI][A-Z&lt;][A-Z&lt;]{3}[A-Z0-9&lt;]{9}[0-9&lt;][A-Z0-9&lt;]{15}){(30)}",
          "StringLengthRange": [30, 30],
          "BaseTextLineSpecificationName": "tls_base"
        },
        {
          "StringRegExPattern": "([0-9]{2}[(01-12)][(01-31)][0-9][MF&lt;][0-9]{2}[0-9&lt;]{4}[0-9][A-Z&lt;]{3}[A-Z0-9&lt;]{11}[0-9]){(30)}",
          "StringLengthRange": [30, 30],
          "BaseTextLineSpecificationName": "tls_base"
        },
        {
          "StringRegExPattern": "([A-Z&lt;]{30}){(30)}",
          "StringLengthRange": [30, 30],
          "BaseTextLineSpecificationName": "tls_base"
        }
      ]
    },
    {
      "Name": "tls_base",
      "CharacterModelName": "MRZ",
      "CharHeightRange": [5, 1000, 1],
      "BinarizationModes": [
        {
          "BlockSizeX": 30,
          "BlockSizeY": 30,
          "Mode": "BM_LOCAL_BLOCK",
          "EnableFillBinaryVacancy": 0,
          "ThresholdCompensation": 15
        }
      ],
      "ConfusableCharactersCorrection": {
        "ConfusableCharacters": [
          ["0", "O"],
          ["1", "I"],
          ["5", "S"]
        ],
        "FontNameArray": ["OCR_B"]
      }
    }
  ],
  "LabelRecognizerTaskSettingOptions": [
    {
      "Name": "task-passport",
      "ConfusableCharactersPath": "ConfusableChars.data",
      "TextLineSpecificationNameArray": ["tls_mrz_passport"],
      "SectionImageParameterArray": [
        {
          "Section": "ST_REGION_PREDETECTION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_LOCALIZATION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_RECOGNITION",
          "ImageParameterName": "ip-mrz"
        }
      ]
    },
    {
      "Name": "task-id",
      "ConfusableCharactersPath": "ConfusableChars.data",
      "TextLineSpecificationNameArray": ["tls_mrz_id_td1", "tls_mrz_id_td2"],
      "SectionImageParameterArray": [
        {
          "Section": "ST_REGION_PREDETECTION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_LOCALIZATION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_RECOGNITION",
          "ImageParameterName": "ip-mrz"
        }
      ]
    },
    {
      "Name": "task-passport-and-id",
      "ConfusableCharactersPath": "ConfusableChars.data",
      "TextLineSpecificationNameArray": ["tls_mrz_passport", "tls_mrz_id_td1", "tls_mrz_id_td2"],
      "SectionImageParameterArray": [
        {
          "Section": "ST_REGION_PREDETECTION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_LOCALIZATION",
          "ImageParameterName": "ip-mrz"
        },
        {
          "Section": "ST_TEXT_LINE_RECOGNITION",
          "ImageParameterName": "ip-mrz"
        }
      ]
    }
  ],
  "CharacterModelOptions": [
    {
      "Name": "MRZ"
    }
  ],
  "ImageParameterOptions": [
    {
      "Name": "ip-mrz",
      "TextureDetectionModes": [
        {
          "Mode": "TDM_GENERAL_WIDTH_CONCENTRATION",
          "Sensitivity": 8
        }
      ],
      "BinarizationModes": [
        {
          "EnableFillBinaryVacancy": 0,
          "ThresholdCompensation": 21,
          "Mode": "BM_LOCAL_BLOCK"
        }
      ],
      "TextDetectionMode": {
        "Mode": "TTDM_LINE",
        "CharHeightRange": [5, 1000, 1],
        "Direction": "HORIZONTAL",
        "Sensitivity": 7
      }
    }
  ],
  "SemanticProcessingOptions": [
    {
      "Name": "sp-passport-and-id",
      "ReferenceObjectFilter": {
        "ReferenceTargetROIDefNameArray": ["roi-passport-and-id"]
      },
      "TaskSettingNameArray": ["dcp-passport-and-id"]
    },
    {
      "Name": "sp-passport",
      "ReferenceObjectFilter": {
        "ReferenceTargetROIDefNameArray": ["roi-passport"]
      },
      "TaskSettingNameArray": ["dcp-passport"]
    },
    {
      "Name": "sp-id",
      "ReferenceObjectFilter": {
        "ReferenceTargetROIDefNameArray": ["roi-id"]
      },
      "TaskSettingNameArray": ["dcp-id"]
    }
  ],
  "CodeParserTaskSettingOptions": [
    {
      "Name": "dcp-passport",
      "CodeSpecifications": ["MRTD_TD3_PASSPORT"]
    },
    {
      "Name": "dcp-id",
      "CodeSpecifications": ["MRTD_TD1_ID", "MRTD_TD2_ID"]
    },
    {
      "Name": "dcp-passport-and-id",
      "CodeSpecifications": ["MRTD_TD3_PASSPORT", "MRTD_TD1_ID", "MRTD_TD2_ID"]
    }
  ]
}`</span><span class="p">;</span>
  <span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">initSettings</span><span class="p">(</span><span class="nx">mrzTemplate</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>添加一个函数，用于从编码为DataURL的图像中识别MRZ。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">capture</span><span class="p">(</span><span class="nx">dataurl</span><span class="p">){</span>
  <span class="kd">let</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">dataurl</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">bytes</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">bytes</span><span class="p">();</span>
  <span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">captureAsync</span><span class="p">(</span><span class="nx">bytes</span><span class="p">,</span> <span class="dl">"</span><span class="s2">ReadPassport</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">let</span> <span class="nx">jsonStr</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">parsedResultItems</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">parsedResultItem</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">parsedResultItems</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="nx">jsonStr</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">parsedResultItem</span><span class="p">.</span><span class="nx">parsed</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">jsonStr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>从renderer接收DataURL消息，并将解析结果发送回给它。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">createWindow</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">win</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BrowserWindow</span><span class="p">({</span>
    <span class="na">width</span><span class="p">:</span> <span class="mi">800</span><span class="p">,</span>
    <span class="na">height</span><span class="p">:</span> <span class="mi">600</span><span class="p">,</span>
    <span class="na">webPreferences</span><span class="p">:</span> <span class="p">{</span>
      <span class="na">devTools</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
      <span class="na">preload</span><span class="p">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">preload.js</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">})</span>
  <span class="nx">ipcMain</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">capture</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">dataurl</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">webContents</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">sender</span>
    <span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">capture</span><span class="p">(</span><span class="nx">dataurl</span><span class="p">);</span>
    <span class="nx">webContents</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">update-result</span><span class="dl">'</span><span class="p">,</span> <span class="nx">result</span><span class="p">);</span>
  <span class="p">})</span>
  <span class="nx">win</span><span class="p">.</span><span class="nx">loadFile</span><span class="p">(</span><span class="dl">'</span><span class="s1">index.html</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<p>在<code class="language-plaintext highlighter-rouge">preload.js</code>中，定义相关函数。</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="p">{</span> <span class="nx">contextBridge</span><span class="p">,</span> <span class="nx">ipcRenderer</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">electron</span><span class="dl">'</span><span class="p">)</span>

<span class="nx">contextBridge</span><span class="p">.</span><span class="nx">exposeInMainWorld</span><span class="p">(</span><span class="dl">'</span><span class="s1">Dynamsoft</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
  <span class="na">onCaptured</span><span class="p">:</span> <span class="p">(</span><span class="nx">callback</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">ipcRenderer</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">update-result</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">_event</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">value</span><span class="p">)),</span>
  <span class="na">capture</span><span class="p">:</span> <span class="p">(</span><span class="nx">dataurl</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">ipcRenderer</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">capture</span><span class="dl">'</span><span class="p">,</span> <span class="nx">dataurl</span><span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>

<p>在<code class="language-plaintext highlighter-rouge">index.js</code>（renderer）中，使用以下函数发送DataURL消息：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">capture</span><span class="p">(</span><span class="nx">dataurl</span><span class="p">);</span>
</code></pre></div></div>

<p>然后，使用以下函数接收解析结果：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">onCaptured</span><span class="p">((</span><span class="nx">value</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">fields</span> <span class="o">=</span> <span class="p">{};</span>
  <span class="kd">let</span> <span class="nx">parsed</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">})</span>
</code></pre></div></div>

<p>好的，我们已经介绍了demo的关键部分。</p>

<h2 id="源代码">源代码</h2>

<p>该项目的源代码可在此处获得：<a href="https://github.com/tony-xlh/electron-passport-scanner">https://github.com/tony-xlh/electron-passport-scanner</a></p>]]></content><author><name>大名维森软件</name></author><category term="文字识别" /><summary type="html"><![CDATA[文章介绍了如何创建一个Electron桌面应用程序来扫描护照。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">如何编写一个Lightning Web Component在Salesforce中扫描条码</title><link href="https://devblogs.damingsoft.com/barcode-scanner-lightning-web-component-in-salesforce/" rel="alternate" type="text/html" title="如何编写一个Lightning Web Component在Salesforce中扫描条码" /><published>2025-02-18T02:29:53+00:00</published><updated>2025-02-18T02:29:53+00:00</updated><id>https://devblogs.damingsoft.com/barcode-scanner-lightning-web-component-in-salesforce</id><content type="html" xml:base="https://devblogs.damingsoft.com/barcode-scanner-lightning-web-component-in-salesforce/"><![CDATA[<p>在本文中，我们将创建一个Lightning Web Component（LWC），用于在著名的CRM系统Salesforce中扫描条码，以便快速输入数据。使用了<a href="https://www.dynamsoft.com/barcode-reader/overview/">Dynamsoft Barcode Reader</a>扫码SDK。</p>

<p>由于Dynamsoft Barcode Reader使用WASM，而WASM默认情况下无法在Salesforce LWC中运行，我们需要使用<code class="language-plaintext highlighter-rouge">iframe</code>嵌入一个条码扫描网页，使用<code class="language-plaintext highlighter-rouge">postMessage</code>在网页和组件之间进行通信。</p>

<p>演示视频：</p>

<video src="https://github.com/user-attachments/assets/d1a3e387-83fc-4dd2-93cd-7adfa53f5149" controls="controls" muted="muted" style="max-width: 100%;max-height:640px; min-height: 200px"></video>

<h2 id="环境配置">环境配置</h2>

<ul>
  <li>安装Salesforce CLI。</li>
  <li>安装Visual Studio Code和Salesforce DX扩展。</li>
</ul>

<p>可以在<a href="https://trailhead.salesforce.com/content/learn/projects/quick-start-lightning-web-components/set-up-salesforce-dx">这里</a>找到详细的指南。</p>

<h2 id="新建salesforce-dx项目">新建Salesforce DX项目</h2>

<p>打开Visual Studio Code，按Ctrl + Shift + P（ Windows ）或Cmd + Shift + P（ macOS ）打开命令面板，然后输入SFDX，选择<code class="language-plaintext highlighter-rouge">Create Project</code>操作。</p>

<p><img src="https://devblogs.damingsoft.com/album/2024/06/salesforce/new_project.jpg" alt="新项目" /></p>

<p>在这里，我们使用标准选项。</p>

<p>然后，运行<code class="language-plaintext highlighter-rouge">SFDX: Create Lightning Web Component</code>，创建名为<code class="language-plaintext highlighter-rouge">barcodeScanner</code>的组件。</p>

<h2 id="创建条码扫描网页">创建条码扫描网页</h2>

<p>创建一个新的HTML文件，命名为<code class="language-plaintext highlighter-rouge">barcode-scanner.html</code>并包含以下内容。可以在<a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&amp;package=cross-platform">此处</a>申请许可证。</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>Barcode Scanner<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;style&gt;</span>
  <span class="nf">#cameraView</span> <span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
    <span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
    <span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nt">&lt;/style&gt;</span>
  <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@10.4.2000/dist/dbr.bundle.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"cameraView"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;script&gt;</span>
    <span class="kd">let</span> <span class="nx">cvRouter</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">cameraEnhancer</span><span class="p">;</span>
    <span class="kd">let</span> <span class="nx">scanning</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
      <span class="dl">"</span><span class="s2">message</span><span class="dl">"</span><span class="p">,</span>
      <span class="k">async</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">received message in iframe</span><span class="dl">"</span><span class="p">);</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">data</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">toggle</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">toggleScanning</span><span class="p">();</span>
        <span class="p">}</span>
      <span class="p">},</span>
      <span class="kc">false</span><span class="p">,</span>
    <span class="p">);</span>

    <span class="nx">initBarcodeScanner</span><span class="p">();</span>
    <span class="k">async</span> <span class="kd">function</span> <span class="nx">initBarcodeScanner</span><span class="p">(){</span>
      <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">License</span><span class="p">.</span><span class="nx">LicenseManager</span><span class="p">.</span><span class="nx">initLicense</span><span class="p">(</span><span class="dl">"</span><span class="s2">LICENSE-KEY</span><span class="dl">"</span><span class="p">);</span>
      <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">Core</span><span class="p">.</span><span class="nx">CoreModule</span><span class="p">.</span><span class="nx">loadWasm</span><span class="p">([</span><span class="dl">"</span><span class="s2">dbr</span><span class="dl">"</span><span class="p">]);</span>
      <span class="nx">cvRouter</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">CVR</span><span class="p">.</span><span class="nx">CaptureVisionRouter</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
      <span class="kd">let</span> <span class="nx">cameraView</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DCE</span><span class="p">.</span><span class="nx">CameraView</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
      <span class="nx">cameraEnhancer</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Dynamsoft</span><span class="p">.</span><span class="nx">DCE</span><span class="p">.</span><span class="nx">CameraEnhancer</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">(</span><span class="nx">cameraView</span><span class="p">);</span>
      <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#cameraView</span><span class="dl">"</span><span class="p">).</span><span class="nx">append</span><span class="p">(</span><span class="nx">cameraView</span><span class="p">.</span><span class="nx">getUIElement</span><span class="p">());</span>
      <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">setInput</span><span class="p">(</span><span class="nx">cameraEnhancer</span><span class="p">);</span>

      <span class="kd">const</span> <span class="nx">resultsContainer</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#results</span><span class="dl">"</span><span class="p">);</span>
      <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">addResultReceiver</span><span class="p">({</span> <span class="na">onDecodedBarcodesReceived</span><span class="p">:</span> <span class="p">(</span><span class="nx">result</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
        <span class="nb">window</span><span class="p">.</span><span class="nx">parent</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span>
      <span class="p">}});</span>
      <span class="nb">window</span><span class="p">.</span><span class="nx">parent</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">({</span><span class="na">initialized</span><span class="p">:</span><span class="kc">true</span><span class="p">});</span>
    <span class="p">}</span>

    <span class="k">async</span> <span class="kd">function</span> <span class="nx">toggleScanning</span><span class="p">()</span> <span class="p">{</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">cvRouter</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Please wait for the initialization...</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">return</span><span class="p">;</span>
      <span class="p">}</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">scanning</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">await</span> <span class="nx">cameraEnhancer</span><span class="p">.</span><span class="nx">open</span><span class="p">();</span>
        <span class="k">await</span> <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">startCapturing</span><span class="p">(</span><span class="dl">"</span><span class="s2">ReadBarcodes_Balance</span><span class="dl">"</span><span class="p">);</span>
      <span class="p">}</span><span class="k">else</span><span class="p">{</span>
        <span class="k">await</span> <span class="nx">cvRouter</span><span class="p">.</span><span class="nx">stopCapturing</span><span class="p">();</span>
        <span class="k">await</span> <span class="nx">cameraEnhancer</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
      <span class="p">}</span>
      <span class="nx">scanning</span> <span class="o">=</span> <span class="o">!</span><span class="nx">scanning</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>它可以打开相机扫描条码，可以接收消息来控制扫描状态，并将条码结果发送回父级页面。</p>

<h2 id="添加静态资源">添加静态资源</h2>

<ol>
  <li>在<code class="language-plaintext highlighter-rouge">staticresources</code>文件夹下新建一个文件夹。</li>
  <li>将<code class="language-plaintext highlighter-rouge">barcode-scanner.html</code>放入这个文件夹。</li>
  <li>
    <p>在<code class="language-plaintext highlighter-rouge">staticresources</code>文件夹中创建一个名为<code class="language-plaintext highlighter-rouge">foldername.resource-meta.xml</code>的元数据文件，并用以下内容描述资源。</p>

    <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;StaticResource</span> <span class="na">xmlns=</span><span class="s">"http://soap.sforce.com/2006/04/metadata"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;cacheControl&gt;</span>Private<span class="nt">&lt;/cacheControl&gt;</span>
    <span class="nt">&lt;contentType&gt;</span>application/zip<span class="nt">&lt;/contentType&gt;</span>
<span class="nt">&lt;/StaticResource&gt;</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="实现lightning-web-component">实现Lightning Web Component</h2>

<p>现在，转到我们创建的Lightning Web Component。</p>

<ol>
  <li>
    <p>在HTML文件中添加元素。</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;template&gt;</span>
  <span class="nt">&lt;lightning-card</span> <span class="na">title=</span><span class="s">"Barcode Scanner"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;lightning-button</span>
              <span class="na">label=</span><span class="s">"Toggle Scanning"</span>
              <span class="na">onclick=</span><span class="s">{toggleScanning}</span>
    <span class="nt">&gt;&lt;/lightning-button&gt;</span>
    <span class="nt">&lt;div&gt;</span>
      <span class="nt">&lt;label&gt;</span>
        Barcode:<span class="ni">&amp;nbsp;</span>
        <span class="nt">&lt;span&gt;</span>{barcodeValue}<span class="nt">&lt;/span&gt;</span>
      <span class="nt">&lt;/label&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"viewer"</span> <span class="na">id=</span><span class="s">"viewer"</span> <span class="na">style=</span><span class="s">"height:500px;width:400px;"</span><span class="nt">&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;/lightning-card&gt;</span>
<span class="nt">&lt;/template&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>组件挂载后，添加iframe。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">dwt</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@salesforce/resourceUrl/dwt</span><span class="dl">'</span><span class="p">;</span> <span class="c1">//import path of the resources folder</span>
<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">BarcodeScanner</span> <span class="kd">extends</span> <span class="nx">LightningElement</span> <span class="p">{</span>
  <span class="nx">initialized</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
  <span class="nx">dbrFrame</span><span class="p">;</span>

  <span class="nx">renderedCallback</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">initialized</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">initialized</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">dbrFrame</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">iframe</span><span class="dl">'</span><span class="p">);</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">dbrFrame</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">dwt</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">/barcode-scanner.html</span><span class="dl">"</span><span class="p">;</span>
    <span class="c1">// div tag in which iframe will be added should have id attribute with value myDIV</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">template</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">div.viewer</span><span class="dl">"</span><span class="p">).</span><span class="nx">appendChild</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dbrFrame</span><span class="p">);</span>
    <span class="c1">// provide height and width to it</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">dbrFrame</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">style</span><span class="dl">"</span><span class="p">,</span><span class="dl">"</span><span class="s2">height:100%;width:100%;</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>单击切换扫描按钮时，通过postMessage控制iframe中页面的扫描状态。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">toggleScanning</span><span class="p">(){</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">dbrFrame</span><span class="p">.</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nx">postMessage</span><span class="p">(</span><span class="dl">"</span><span class="s2">toggle</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>接收条码结果。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">barcodeResult</span><span class="p">;</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
  <span class="dl">"</span><span class="s2">message</span><span class="dl">"</span><span class="p">,</span>
  <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">event</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">barcodeResultItems</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">barcodeResult</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">barcodeResultItems</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="kc">false</span><span class="p">,</span>
<span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>显示条码文本。</p>

    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">get</span> <span class="nx">barcodeValue</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">barcodeResult</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">barcodeResult</span><span class="p">.</span><span class="nx">text</span><span class="p">;</span>
  <span class="p">}</span><span class="k">else</span><span class="p">{</span>
    <span class="k">return</span> <span class="dl">""</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="源代码">源代码</h2>

<p>下载源代码并尝试使用：</p>

<p><a href="https://github.com/tony-xlh/Dynamic-Web-TWAIN-samples/tree/main/Salesforce/webTWAIN">https://github.com/tony-xlh/Dynamic-Web-TWAIN-samples/tree/main/Salesforce/webTWAIN</a></p>]]></content><author><name>大名维森软件</name></author><category term="条码扫描" /><summary type="html"><![CDATA[文章分享了如何编写一个用于扫描条码的Salesforce Lightning Web Component。]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://devblogs.damingsoft.com/assets/og/og-default.png" /><media:content medium="image" url="https://devblogs.damingsoft.com/assets/og/og-default.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>