跳轉到主要內容
此情境僅適用於 Windows。
在此情境中,ABBYY FineReader Engine 會用於「掃描電腦」上,負責掃描影像並將其儲存為檔案。 此情境可作為其他情境中,文件處理前期階段的一部分,也就是用來取得文件的電子版本,以便後續處理。使用範例包括為歸檔而掃描文件、取得文件的可編輯版本,以及從文件中擷取特定資料。 紙本文件經掃描後,影像會以電子格式儲存,從而產生高品質的紙本文件電子版。 文件可能會經過下列處理階段:
  1. 掃描
文件可透過掃描器提供的兩種掃描介面之一 (TWAIN 或 WIA) 進行掃描,也可以使用 ABBYY 自有的掃描介面,或在沒有掃描介面的情況下進行掃描。
  1. 掃描影像的前處理
掃描完成後,影像可進行前處理。前處理包括去除雜點、校正扭曲的文字行、色彩反轉、移除黑邊,以及校正影像方向或解析度。對開頁面可拆分為兩張獨立影像。處理後的影像可儲存為各種影像格式,例如 JPEG、TIFF、BMP。

情境實作

以下將詳細說明在此情境中使用 ABBYY FineReader Engine 12 的建議方式。在建議的情境實作中,會省略影像準備階段。關於如何實作影像準備的提示,請參閱下方的其他最佳化
若要開始使用 ABBYY FineReader Engine,您需要建立 Engine 物件。Engine 物件是 ABBYY FineReader Engine 物件階層中的最高層物件,提供各種全域設定、一些處理方法,以及建立其他物件的方法。若要建立 Engine 物件,您可以使用 InitializeEngine 函式。另請參閱 其他載入 Engine 物件的方法 (Win) 。

C#

public class EngineLoader : IDisposable
{
    public EngineLoader()
    {
        // 使用 FREngine.dll 的完整路徑、您的 Customer Project ID,
        // 以及(如適用)Online License 權杖檔案的路徑和 Online License 密碼來初始化這些變數
        string enginePath = "";
        string customerProjectId = "";
        string licensePath = "";
        string licensePassword = "";
        // 載入 FREngine.dll 程式庫
        dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
           
        try
        {
            if (dllHandle == IntPtr.Zero)
            {
                throw new Exception("Can't load " + enginePath);
            }
            IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine");
            if (initializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find InitializeEngine function");
            }
            IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
            if (deinitializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DeinitializeEngine function");
            }
            IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
            if (dllCanUnloadNowPtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DllCanUnloadNow function");
            }
            // 將指標轉換為委派
            initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
                initializeEnginePtr, typeof(InitializeEngine));
            deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
                deinitializeEnginePtr, typeof(DeinitializeEngine));
            dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
                dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
            // 呼叫 InitializeEngine 函式 
            // 傳入 Online License 檔案路徑和 Online License 密碼
            int hresult = initializeEngine(customerProjectId, licensePath, licensePassword, 
                "", "", false, ref engine);
            Marshal.ThrowExceptionForHR(hresult);
        }
        catch (Exception)
        {
            // 釋放 FREngine.dll 程式庫
            engine = null;
            // 在呼叫 FreeLibrary 前先刪除所有物件
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            FreeLibrary(dllHandle);
            dllHandle = IntPtr.Zero;
            initializeEngine = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            throw;
        }
    }
    // Kernel32.dll 函式
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // FREngine.dll 函式
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine(string customerProjectId, string licensePath, 
        string licensePassword, string tempFolder, string dataFolder, bool isSharedCPUCoresMode, 
        ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // 私有變數
    private FREngine.IEngine engine = null;
    // FREngine.dll 的控制代碼
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}
ABBYY FineReader Engine 提供了 ScanManager 物件,用於管理掃描來源。該物件的 ScanSources 屬性可存取所有可用掃描器的清單,而 FindScanSources 方法則允許您依掃描器所提供的 API 類型以及用於設定掃描選項的使用者介面類型來篩選掃描器。若要掃描檔案並將其儲存至磁碟,您可以使用 ScanSource 物件的下列兩種方法之一:Scan 方法會等待掃描完成後才回傳;BeginScan 方法則會啟動非同步掃描作業並立即回傳。您可以實作 IScanCallback 介面,並透過它接收掃描進度的通知。以下參數可透過 ABBYY FineReader Engine 12 API 存取:亮度、色彩、解析度、影像壓縮類型、影像旋轉角度、掃描區域大小、雙面掃描模式、自動進紙模式、頁面間暫停,以及其他更多設定。掃描參數可透過 ScanSource object 的 ScanSettings 屬性進行設定。此屬性用於存取 ScanSourceSettings object,進而提供對該來源掃描設定的存取。若要同步掃描影像:
  1. 建立一個 ScanManager 物件。您可以透過 CreateScanManager method 的輸入參數,指定是否要寫入掃描日誌。
  2. 使用 ScanManager 物件的 FindScanSources 方法選擇掃描來源,並指定掃描器應支援的 API 和 UI 類型。
  3. 如果您選擇不顯示讓使用者自行設定掃描選項的對話方塊,請使用所選 ScanSourceScanSettings 屬性來調整掃描選項。請在 ScanSourceSettings 物件的對應屬性中,為亮度、解析度及其他參數設定適當的值。您可以使用 ScanSource 的 Capabilities 屬性,檢查此掃描器有哪些可用設定。
  4. 指定用來儲存掃描頁面的資料夾名稱。資料夾名稱應為 BSTR 變數,例如 ScanFolder。
  5. 執行 ScanSource 物件的 Scan 方法,並將要顯示給使用者的對話方塊類型作為參數傳入 (若不顯示任何對話方塊,請傳遞 SSUIT_None 常數) ,以及將 ScanFolder 路徑指定為存放結果的資料夾路徑。
  6. 此方法會將影像檔名稱以 StringsCollection 物件傳回。您可以從這個 StringsCollection 物件取得影像檔名稱,然後像處理一般影像檔一樣處理這些檔案。
若要非同步掃描影像:
  1. 建立一個實作 IScanCallback 介面的物件。對於非同步掃描作業,來自回呼介面的通知非常有用。
  2. 建立 ScanManager 物件。您可以透過 CreateScanManager 方法的輸入參數,指定是否寫入掃描記錄。
  3. 透過 ScanManager 物件的 FindScanSources 方法選擇掃描來源,並指定掃描器應支援的 API 與 UI 類型。
  4. 如果您選擇不顯示讓使用者自行設定掃描選項的對話方塊,請使用所選 ScanSource 的 ScanSettings 屬性來調整掃描選項。請在 ScanSourceSettings 物件的對應屬性中,為亮度、解析度及其他參數設定適當的值。您可以使用 ScanSource 的 Capabilities 屬性,檢查此掃描器提供哪些可用設定。
  5. 指定用來儲存掃描頁面的資料夾名稱。資料夾名稱應為 BSTR 變數,例如 ScanFolder。
  6. 執行 ScanSource 物件的 BeginScan 方法,並將要顯示給使用者的對話方塊類型作為參數傳入 (傳遞 SSUIT_None 常數即可不顯示任何對話方塊) ,以及將 ScanFolder 路徑設為存放結果的資料夾路徑。您也需要傳遞您所建立之回呼物件的指標。
  7. 影像檔案的路徑會透過 IScanCallback 介面的 OnImageScanned 通知傳回,而作業完成則由 OnScanComplete 通知表示。由於這些 method 的實作由您提供,因此您可以自行決定在各種情況下要執行什麼動作。例如,收到影像檔案路徑後,您可以像處理磁碟上的其他檔案一樣處理它,並以一般方式將其載入 FineReader Engine 進行處理。
在掃描完成並收到 OnScanComplete 通知之前,您無法再次執行 BeginScan 或 Scan,即使是對其他掃描器也一樣。非同步掃描的優點不在於您可以同時執行多個掃描作業,而在於可在掃描新影像的同時,處理已接收的影像。
以下是非同步掃描情境的範例程式碼:

C#

// 實作 IScanCallback 介面
class ScanningCallback :FREngine.IScanCallback
{
  // 建構函式
  public ScanningCallback() {}
  ...
  // 提供 IScanCallback 方法的實作
  public void OnError(string sourceName, string message) { // 在此回報或處理錯誤 }
  public void OnImageScanned(string sourceName, string Path, ref bool cancel) { // 在此加入掃描影像時所需執行的動作 }
  public void OnScanComplete() { // 在此加入掃描完成後要執行的動作 }
  // 私有變數
  ...
}
// 建立掃描管理員,並停用記錄
scanManager = engineLoader.Engine.CreateScanManager(false);
// 建立依 API 和 UI 類型篩選的掃描來源清單
FREngine.IScanSources sources = scanManager.FindScanSources(UIType, APIType);
// 以此為例,從集合中選取第一個掃描來源
FREngine.IScanSource selectedSource = sources[0];
// 存取掃描來源的設定與功能
FREngine.IScanSourceSettings settings = selectedSource.ScanSettings;
FREngine.IScanSourceCapabilities capabilities = selectedSource.Capabilities;
// 若掃描器支援雙面模式,則將掃描選項設為雙面模式
settings.DuplexMode = capabilities.HasDuplexMode;
// 設定多頁掃描選項,並設定頁面間 10 秒的延遲
settings.MultipleImagesEnabled = true;
settings.PauseBetweenPagesMode = FREngine.ScanPauseModeEnum.SPM_PresetDelay;
settings.Delay = 10;
// 設定存放結果的資料夾路徑
string scanFolder = "D:\\SampleImages";
// 建立回呼物件
IScanningCallback callback = new ScanningCallback();
// 開始掃描
selectedSource.BeginScan(SSUIT_None, scanFolder, callback);
完成 ABBYY FineReader Engine 的作業後,您需要卸載 Engine 物件。為此,請使用匯出的 DeinitializeEngine 函式。

C#

public class EngineLoader : IDisposable
{
    // 卸載 FineReader Engine
    public void Dispose()
    {
        if (engine == null)
        {
            // Engine 尚未載入
            return;
        }
        engine = null;
        // 在呼叫 FreeLibrary 之前刪除所有物件
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        int hresult = deinitializeEngine();
 
        hresult = dllCanUnloadNow();
        if (hresult == 0)
        {
            FreeLibrary(dllHandle);
        }
        dllHandle = IntPtr.Zero;
        initializeEngine = null;
        deinitializeEngine = null;
        dllCanUnloadNow = null;
        // 清理完成後擲回例外狀況
        Marshal.ThrowExceptionForHR(hresult);
    }
    // Kernel32.dll 函式
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // FREngine.dll 函式
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine( string customerProjectId, string LicensePath, string LicensePassword, , , , ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // 私有變數
    private FREngine.IEngine engine = null;
    // FREngine.dll 的控制代碼
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}

必要資源

您可以使用 FREngineDistribution.csv 檔案,自動建立應用程式正常運作所需檔案的清單。若要依此情境進行處理,請在第 5 欄 (RequiredByModule) 中選取下列值: Core Core.Resources Opening Opening, Processing Opening.Scanning 如果您修改了標準情境,請相應調整所需模組。您也需要指定介面語言、辨識語言,以及應用程式使用的任何其他功能 (例如,若您需要辨識 CJK languages 的文字,則還需使用 Processing.OCR.CJK) 。如需更多資訊,請參閱 Working with the FREngineDistribution.csv File

其他最佳化

以下是說明檔中可找到更多資訊的章節,說明如何為各個處理階段設定參數:
  • 文件分隔
    • 在此情境中,您可能需要將傳入的影像分組成文件。例如,您可能知道每份文件的頁數,或確保在第一份文件的最後一頁與下一份文件的第一頁之間有附帶條碼的分隔頁。請參閱條碼辨識情境

另請參閱

基本使用案例實作