跳轉到主要內容
處理紙本文件時,您需要找出並更正錯誤或刻意做出的變更。使用 Document Comparison API 可快速且有效地找出這些變更。 此情境用於比較特別重要的文件 (例如合約和銀行文件) 及其副本。比較結果包含內容類型 (僅文字) 、修改類型 (刪除、插入或修改) ,以及它們在原件和副本中的位置資訊。您可以取得偵測到的差異清單,或任何變更所在的區域,並將比較結果儲存至外部檔案,以便後續處理或長期保存。 若要比較文件或頁面,透過掃描取得或以電子格式儲存的檔案通常會經過數個處理階段,而每個階段都有各自的特性:
  1. 已掃描檔案或影像的預處理
如果檔案及其副本包含某些瑕疵或刻意加入的標記,例如簽名或印章,則在辨識前需要先進行一些預處理。
  1. 透過完整還原文件結構與格式進行辨識
辨識文件時,會識別文件中的各種版面元素 (文字、表格、影像、分隔線等) 。在文件合成過程中,會還原文件的邏輯結構,而頁面合成則可完整還原文件格式 (字型、樣式等) 。
  1. 文件或頁面比較
若要將文件或頁面與其副本進行比較,請使用經 ABBYY FineReader Engine 辨識的檔案。您可以使用同一份文件的兩個不同格式版本。比較完成後,您會取得包含變更清單的結果,並可利用該結果擷取變更位置資訊。若您使用人工驗證,可利用這些資訊在文字中標示變更,讓操作員更容易完成工作。
  1. 匯出為外部格式
您也可以將比較結果儲存為 XML 和 DOCX 格式。 下方所述程序也可參考 Linux 與 macOS 的 Document Comparison 範例,以及 Windows 的 Document Comparison 示範工具。

情境實作

本主題中提供的程式碼範例僅適用於 Windows。
以下將詳細介紹在此情境中使用 ABBYY FineReader Engine 的建議方式。
若要開始使用 ABBYY FineReader Engine,您需要建立 Engine 物件。Engine 物件是 ABBYY FineReader Engine 物件階層中的最上層物件,可提供各種全域設定、部分處理方法,以及建立其他物件的方法。若要建立 Engine 物件,您可以使用 InitializeEngine 函式。另請參閱 載入 Engine 物件的其他方式 (Win) 。

C#

public class EngineLoader : IDisposable
{
    public EngineLoader()
    {
        // 使用 FREngine.dll 的完整路徑、您的 Customer Project ID,
        // 以及(如適用)線上授權權杖檔案的路徑和線上授權密碼來初始化這些變數
        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 函式 
            // 並傳入線上授權檔案路徑和線上授權密碼
            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 提供 FRDocument 物件,可用來處理多頁文件。使用此物件可保留文件的邏輯結構,並維持原始文字、欄位、字型、樣式等內容。若您想比較頁面,請使用 FRPage 物件。若要載入單一文件的影像並進行預處理,您應建立 FRDocument 物件,並將影像加入其中。您可以採用下列任一方式:

C#

// 從影像檔建立 FRDocument 物件
FREngine.IFRDocument frDocument = engine.CreateFRDocumentFromImage( "C:\\MyImage.tif", null );
若要辨識文件,我們建議您使用 FRDocument 物件的分析與辨識方法。此物件提供一整套方法,可用於文件分析、辨識與合成。最方便的方法是 Process 方法,可一次完成文件分析、辨識與合成。它也能以最高效率運用多處理器與多核心系統的平行處理功能。不過,您也可以使用 PreprocessAnalyzeRecognizeSynthesize 方法,依序執行預處理、分析、辨識與合成。
您可以載入適合的預先定義設定檔,為文件設定辨識參數 (如需更多資訊,請參閱 Working with Profiles) 。

C#

// 使用預設參數處理文件
// 如有需要,您可以變更這些參數,例如先載入設定檔
frDocument.Process( null );
若要比較文件或頁面與其副本:
  1. 請確認您的 ABBYY FineReader Engine 授權支援 Compare Documents 模組。
  2. 使用 Engine 物件的 CreateComparator 方法建立 Comparator 物件。
  3. [選用] 使用 ComparisonParams 物件,將屬性設為所需的值。
  4. 呼叫 Comparator 物件的 CompareDocuments 方法,比較原始文件與副本。您將收到一個 ComparisonResult 物件,其中包含偵測到的變更資訊。

C#

// 執行文件比較 
FREngine.IComparator comparator = engine.CreateComparator();
FREngine.IComparisonResult comparatorResult = 
    comparator.CompareDocuments( referenceFRDocument, userFRDocument, null, null );
ComparisonResult 物件包含完整的差異清單,並提供取得個別頁面差異的方法。您可以使用 GetChangesForReferencePageGetChangesForUserPage 方法,取得原始文件及其副本中的變更。使用 ChangeLocation 物件可取得變更位置的資訊,並使用其 RegionForPage 屬性取得指定頁面上的變更區域。

C#

// 取得偵測到的修改及其在原始文件中的位置資訊
FREngine.IChanges changes = comparatorResult.Changes;
foreach( FREngine.IChange change in changes ) {
      FREngine.ModificationTypeEnum modificationType = change.ModificationType;
      FREngine.IChangeLocation referenceLocation = change.ReferenceLocation;
      // 現在您可以在頁面上標示這些變更,供人工審核人員檢查
      ... 
}
若要匯出比較結果,請呼叫 ComparisonResult 物件的 Export 方法,並將檔案路徑作為輸入參數傳入。資料可儲存為 XML,或儲存為含有追蹤修訂的 DOCX 檔案。C#
// 儲存為 XML 格式
comparisonResult.Export( "C:\\ComparisonResult.xml", FREngine.ComparatorExportFormatEnum.CEF_Xml, null );
完成 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 Processing Processing.OCR Processing.OCR, Processing.ICR Processing.OCR.NaturalLanguages Processing.OCR.NaturalLanguages, Processing.ICR.NaturalLanguages Export Export, Processing 如果您修改了標準情境,請相應調整所需模組。您也需要指定應用程式所使用的介面語言、辨識語言,以及任何其他附加功能 (例如,如果您需要開啟 PDF 檔案,則為 Opening.PDF;如果您需要辨識 CJK 語言 的文字,則為 Processing.OCR.CJK) 。如需更多資訊,請參閱 Working with the FREngineDistribution.csv File

特定工作的其他最佳化

以下概述說明主題,其中包含在不同處理階段自訂設定的其他資訊:
  • 掃描 - 僅限 Windows
    • 掃描
      說明 ABBYY FineReader Engine 的文件掃描情境。
  • 辨識
    • 預處理、分析、辨識與合成的參數調校
      使用分析、辨識和合成參數物件自訂文件處理。
    • PageProcessingParams 物件
      此物件可讓您自訂分析與辨識參數。您可以使用此物件指定必須偵測的影像與文字特徵 (反相影像、方向、條碼、辨識語言、辨識錯誤容差) 。
    • SynthesisParamsForPage 物件
      此物件包含在合成期間負責還原頁面格式的參數。
    • SynthesisParamsForDocument 物件
      此物件可讓您自訂文件合成:還原其結構與格式。
    • MultiProcessingParams 物件 - 已針對 Linux 和 Windows 實作
      處理大量影像時,同時處理會很有幫助。在此情況下,處理負載會在影像開啟與預處理、版面分析、辨識及匯出期間分散到各個處理器核心,從而加快處理速度。
      讀取模式 (同時或循序) 可使用 MultiProcessingMode 屬性設定。RecognitionProcessesCount 屬性則可控制可啟動的程序數量。

另請參見

基本使用案例實作