天天看點

使用計算機視覺深入學習和建立現代OCR管道

在這篇文章中,我們将帶您了解我們如何為[【移動文檔掃描器】建構最先進的光學字元識别(OCR)管道的幕後故事。我們使用了計算機視覺和深度學習的進步,如雙向長短期記憶(LSTM),連接配接主義時間分類(CTC),卷積神經網絡(CNN)等。此外,我們還将深入研究如何實際使我們的 OCR 管道在 Dropbox 規模上做好生産準備。

文檔掃描器可以使用手機拍照并[“掃描”]收據和發票等項目。我們的移動文檔掃描器僅輸出圖像 - 就計算機而言,圖像中的任何文本都隻是一組像素,無法複制粘貼,搜尋或您可以對文本執行的任何其他操作。

是以,需要應用光學字元識别或OCR。此過程從我們的文檔掃描圖像中提取實際文本。運作 OCR 後,我們可以為 Dropbox Business 使用者啟用以下功能:

  • 提取掃描文檔中的所有文本并為其編制索引,以便以後可以搜尋
  • 建立隐藏的疊加層,以便可以從另存為 PDF 的掃描件中複制和粘貼文本

當我們建構移動文檔掃描器的第一個版本時,我們使用了一個商用現成的 OCR 庫,以便在深入建立我們自己的基于機器學習的 OCR 系統之前進行産品驗證。這意味着将商業系統內建到我們的掃描管道中,為我們的業務使用者提供上述兩種功能,以檢視他們是否從OCR中找到了足夠的用途。一旦我們确認使用者對移動文檔掃描器和OCR的需求确實很強烈,我們決定建構自己的内部OCR系統,原因有幾個。

首先,有一個成本考慮:擁有自己的OCR系統将為我們節省大量資金,因為許可的商業OCR SDK根據掃描次數向我們收費。其次,商業系統針對平闆掃描器圖像的傳統OCR世界進行了調整,而我們的操作場景要困難得多,因為手機照片更加不受限制,具有褶皺或彎曲的文檔,陰影和不均勻的照明,模糊和反射高光等。是以,我們可能有機會提高識别準确性。

事實上,計算機視覺世界發生了翻天覆地的變化,這給了我們一個獨特的機會。傳統上,OCR系統是大量流水線的,手工建構和高度調整的子產品利用了各種條件,它們可以假設使用平闆掃描器捕獲的圖像是正确的。例如,一個子產品可能查找文本行,然後下一個子產品将查找單詞和段字母,然後另一個子產品可能會對字元的每個部分應用不同的技術來找出字元是什麼,依此類推。大多數方法都依賴于輸入圖像的二值化作為早期階段,這可能很脆弱,并丢棄了重要的線索。建構這些 OCR 系統的過程非常專業且勞動密集型,系統通常隻能處理來自平闆掃描器的相當有限的影像。

在過去的幾年中,深度學習成功地應用于計算機視覺中的許多問題,這些問題為我們提供了強大的新工具來處理OCR,而不必複制過去複雜的處理管道,而是依靠大量資料讓系統自動學習如何執行許多以前手動設計的步驟。

也許建構我們自己的系統的最重要原因是,它将讓我們更好地控制自己的命運,并允許我們在未來開發更多創新功能。

在本部落格文章的其餘部分,我們将帶您了解我們如何以 Dropbox 規模建構此管道的幕後故事。大多數商業機器學習項目遵循三個主要步驟:

  1. 研究和原型設計,看看是否有可能
  2. 為實際最終使用者生産模型
  3. 在現實世界中完善系統

我們将依次引導您完成這些步驟中的每一個。

研究和原型設計

我們最初的任務是看看我們是否可以建構一個最先進的OCR系統。

我們首先收集了一組具有代表性的捐贈文檔圖像,這些圖像與使用者可能上傳的内容相比對,例如收據,發票,信件等。為了收集這一組,我們詢問了一小部分使用者,他們是否會捐贈一些圖像檔案供我們改進算法。在Dropbox,我們非常重視使用者隐私,是以明确表示這是完全可選的,如果捐贈,檔案将保持私密和安全。我們對此類使用者捐贈的資料使用各種安全預防措施,包括從不将捐贈的資料儲存在本地計算機上的永久存儲中,維護廣泛的審計,需要強大的身份驗證才能通路其中任何資料,等等。

對于使用者捐贈的資料,另一個重要的、特定于機器學習的元件是如何标記它。大多數目前的機器學習技術都是受到嚴格監督的,這意味着它們需要顯式手動标記輸入資料,以便算法可以學習自己進行預測。傳統上,這種标簽是由外部勞工完成的,通常使用微工作平台,如亞馬遜的Mechanical Turk(MTurk)。但是,使用MTurk的缺點是每個項目都可能被不同的從業人員看到和标記,我們當然不希望像這樣在野外公開使用者捐贈的資料!

是以,我們在Dropbox的團隊建立了我們自己的資料注釋平台,名為DropTurk。DropTurk可以向MTurk(如果我們處理公共非使用者資料)或一小群雇用的承包商送出标簽作業,以擷取使用者捐贈的資料。這些承包商遵守嚴格的保密協定(NDA),以確定他們不能保留或共享他們标記的任何資料。DropTurk包含注釋任務UI模闆的标準清單,我們可以針對新資料集和标記任務快速組裝和自定義這些模闆,這使我們能夠非常快速地注釋資料集。

例如,下面是一個 DropTurk UI,旨在為單個單詞圖像提供真實資料,包括以下選項之一供從業人員完成:

  • 轉錄圖像中的實際文本
  • 标記單詞的方向是否正确
  • 标記它是否為非英語腳本
  • 标記它是否不可讀或不包含任何文本
使用計算機視覺深入學習和建立現代OCR管道

DropTurk UI,用于為單詞圖像添加地面實況資料

我們的DropTurk平台包括儀表闆,可檢視過去作業的概述,觀察目前作業的進度并安全地通路結果。此外,我們可以進行分析以評估員工的表現,甚至可以對正在進行的工作進行注釋進行員工級别的圖形監控,以便及早發現潛在問題:

使用計算機視覺深入學習和建立現代OCR管道

DropTurk Dashboard

使用DropTurk,我們收集了一個單詞級資料集,其中包含單個單詞及其注釋文本的圖像,以及一個完整的文檔級資料集,其中包含完整文檔(如收據)和完全轉錄文本的圖像。我們使用後者來測量現有最先進的OCR系統的準确性;然後,這将通過告訴我們必須滿足或擊敗我們自己的系統而達到或擊敗的分數來告知我們的努力。在這個特定的資料集上,我們必須達到的準确率是在90年代中期。

我們的第一個任務是确定 OCR 問題是否能在合理的時間内得到解決。是以,我們将 OCR 問題分為兩部分。首先,我們将使用計算機視覺來擷取文檔的圖像并将其分割成行和單詞;我們稱之為單詞檢測器。然後,我們将每個單詞輸入到深網中,将單詞圖像轉換為實際文本;我們稱之為“深網”這個詞。

我們認為Word Detector會相對簡單,是以我們首先将精力集中在Word Deep Net上,我們不太确定。

字深網

Word Deep Net結合了計算機視覺和自動語音識别系統中使用的神經網絡架構。裁剪單詞的圖像被輸入到具有多個卷積層的卷積神經網絡(CNN)中。然後,CNN輸出的視覺特征作為序列饋送到雙向LSTM(長短期記憶)(在語音識别系統中很常見),這使我們的單詞“片段”有意義,最後使用連接配接主義時間分類(CTC)層進行文本預測。在适當的情況下使用批處理規範化。

使用計算機視覺深入學習和建立現代OCR管道

OCR 字深網

一旦我們決定了将單個單詞的圖像轉換為文本的網絡架構,我們就需要弄清楚如何收集足夠的資料來訓練它。深度學習系統通常需要大量的訓練資料才能獲得良好的識别性能。事實上,訓練資料量往往是目前系統中最重要的瓶頸。通常,所有這些資料都必須收集然後手動标記,這是一個耗時且昂貴的過程。

另一種方法是以程式設計方式生成訓練資料。然而,在大多數計算機視覺問題中,目前很難為訓練算法生成足夠逼真的圖像:成像環境和轉換的多樣性太大,無法有效模拟。(目前研究的一個有前途的領域是生成對抗網絡(GAN),它似乎非常适合生成現實資料。幸運的是,在這種情況下,我們的問題是使用合成資料的完美比對,因為我們需要生成的圖像類型非常受限制,是以可以自動渲染。與自然或大多數人造物體的圖像不同,文檔及其文本是合成的,并且單個字元的可變性相對有限。

我們的合成資料管道由三部分組成:

  1. 包含要使用的單詞的語料庫
  2. 用于繪制單詞的字型集合
  3. 一組用于模拟真實世界扭曲的幾何和光度變換

生成算法隻是從每個樣本中抽取樣本,以建立一個獨特的訓練示例。

使用計算機視覺深入學習和建立現代OCR管道

合成生成的單詞圖像

我們從這三個字型開始,單詞來自19世紀的古騰堡計劃書籍,我們收集了大約一千種字型,以及一些簡單的扭曲,如旋轉,下劃線和模糊。我們生成了大約一百萬個合成單詞,訓練了我們的深度網絡,然後測試了我們的準确性,大約是79%。這沒關系,但還不夠好。

通過多次疊代,我們以多種方式發展了合成資料管道的每一部分,以提高識别準确性。一些亮點:

  • 我們注意到我們在收據上做得不好,是以我們擴充了我們的單詞語料庫,以包括統一産品代碼(UPC)資料庫,該資料庫包含諸如“24QT TISSUE PAPER”之類的條目,這些條目通常出現在收據上。
  • 我們注意到網絡在處理具有斷開連接配接的片段的字母時遇到了困難。這揭示了一個更深層次的問題:收據通常使用帶有斑點,斷開連接配接或墨水污迹的字母的熱敏字型列印,但是我們的網絡隻獲得了具有平滑連續字型(如來自雷射列印機)或輕度位映射字元(如螢幕截圖)的訓練資料。為了解決這一缺點,我們最終找到了中國的一家字型供應商,他們可以為我們提供具有代表性的古代熱敏列印機字型。
使用計算機視覺深入學習和建立現代OCR管道

使用不同的熱敏列印機字型合成生成的單詞,在收據中很常見

  • 我們的字型選擇過程最初太天真了。我們最終手工選擇了大約2000種字型。并非所有字型都得到同等使用。是以,我們對世界上排名前50位的字型進行了研究,并建立了一個字型頻率系統,使我們能夠更頻繁地從常見字型(如Helvetica或Times New Roman)中采樣,同時仍然保留長尾稀有字型(例如一些華麗的徽标字型)。此外,我們發現某些字型的符号不正确或支援有限,導緻隻有正方形,或者它們的小寫或大寫字母不比對,是以不正确。我們必須手動周遊所有兩千種字型,并标記那些具有無效符号,數字或大小寫的字型,這樣我們就不會無意中用不正确的資料訓練網絡。
  • 上遊單詞檢測器(稍後描述)經過調整,可提供高召回率和低精度。在圖像中查找文本過于熱心,這樣它就不會錯過任何實際文本(高召回率),而犧牲了經常“找到”實際上不存在的單詞(低精度)。這意味着Word Deep Net必須處理大量具有噪聲的基本上空白的圖像。是以,我們讓合成資料管道生成具有代表性的負訓練示例,其中包含空的地面真值字元串,包括常見的紋理背景,如木材、大理石台面等。
使用計算機視覺深入學習和建立現代OCR管道

合成生成的否定訓練示例

  • 從合成生成的單詞的直方圖中,我們發現許多符号被低估了,例如/或&。我們通過合成生成具有代表性的日期、價格、URL 等,人為地提高了合成語料庫中這些内容的頻率。
  • 我們添加了大量的視覺轉換,例如翹曲、假陰影和假折痕等等。
使用計算機視覺深入學習和建立現代OCR管道

假陰影效果

資料與使用的機器學習模型一樣重要,是以我們花費了大量時間來優化此資料生成管道。在某個時候,我們将開源并釋出這些合成生成的資料,供其他人訓練和驗證他們自己的系統和研究。

我們在 Amazon EC2 G2 GPU 執行個體上訓練了我們的網絡,并行啟動了許多實驗。我們所有的實驗都放在一個實驗室筆記本中,其中包括複制實驗所需的一切,以便我們可以跟蹤意外的準确性凸起或損失。

我們的實驗室筆記本包含編号實驗,最新的實驗是第一個。它跟蹤了機器學習可重複性所需的所有内容,例如所用代碼的唯一 git 哈希、指向 S3 的指針以及生成的資料集和結果、評估結果、圖形、該實驗目标的進階描述等。當我們建構合成資料管道和訓練網絡時,我們還建構了許多專用工具來可視化字型、調試網絡猜測等。

使用計算機視覺深入學習和建立現代OCR管道

示例早期實驗跟蹤錯誤率與我們的Word Deep Net訓練了多長時間,與僅由單個單詞組成的評估資料集(單字準确性)

我們早期的實驗跟蹤了Word Deep Net在單個單詞的OCR圖像上的表現,我們稱之為單字準确性(SWA)。在這種情況下,準确性意味着深度網絡有多少基本真理詞是正确的。此外,我們還跟蹤了網絡的精度和召回率。精度是指深度網絡傳回的實際上正确的單詞的比例,而召回率是指深度網絡正确預測的評估資料的比例。在精度和召回率之間往往存在權衡。

例如,假設我們有一個機器學習模型,旨在将電子郵件分類為垃圾郵件。精确度将是分類器是否所有标記為垃圾郵件的東西,有多少實際上是垃圾郵件?相比之下,回想一下,在所有真正是垃圾郵件的東西中,我們是否标記了多少?可以正确标記垃圾郵件(高精度),同時不實際标記所有真正的垃圾郵件(低召回率)。

每周,我們跟蹤我們做得有多好。我們将資料集劃分為不同的類别,例如register_tapes(收據),螢幕截圖,scanned_docs等,并為每個類别單獨計算準确性,并計算所有資料的整體準确性。例如,下面的條目顯示了我們實驗室筆記本中第一次完整的端到端測試的早期工作,其中一個真正的單詞檢測器與我們真正的Word Deep Net相結合。你可以看到,我們在一開始做得非常糟糕:

使用計算機視覺深入學習和建立現代OCR管道

我們實驗室筆記本中早期端到端實驗的螢幕截圖

在某一時刻,我們的合成資料管道在我們的OCR基準集上産生了高80年代的單字準确性(SWA)百分比,我們決定完成這一部分。然後,我們收集了大約20,000個真實的單詞圖像(相比之下,我們的100萬個合成生成的單詞),并使用這些圖像來微調Word Deep Net。這把我們帶到了90年代中期的SWA。

我們現在有一個系統,可以在單個單詞圖像上做得很好,但當然,一個真正的OCR系統可以對整個文檔的圖像進行操作。我們的下一步是專注于文檔級的單詞檢測器。

字檢測器

對于我們的 Word Detector,我們決定不使用基于網絡的深度方法。這種方法的主要候選者是對象檢測系統,如RCNN,它試圖從圖像中檢測狗,貓或植物等物體的位置(邊界框)。大多數圖像可能隻有給定對象的一到五個執行個體。

然而,大多數文檔不僅隻有幾個單詞 - 它們有數百甚至數千個單詞,即比當時大多數基于神經網絡的對象檢測系統能夠找到的對象多幾個數量級。是以,我們不确定這樣的算法是否會擴充到我們的OCR系統所需的水準。

另一個重要的考慮因素是,使用特征檢測器的傳統計算機視覺方法可能更容易調試,因為神經網絡是出了名的不透明,并且具有難以了解和解釋的内部表示。

我們最終使用了一種經典的計算機視覺方法,稱為最大穩定極值區域(MSER),使用OpenCV的實作。MSER 算法查找圖像的不同門檻值或級别處的連接配接區域。從本質上講,它們檢測圖像中的斑點,是以特别适合文本。

我們的單詞檢測器首先檢測圖像中的 MSER 特征,然後将這些特征串聯成單詞和行檢測。一個棘手的方面是,我們的單詞深度網絡接受固定大小的單詞圖像輸入。這要求字檢測器有時在單個檢測框中包含多個單詞,或者如果單個單詞太長而不适合深度網絡的輸入大小,則将其切成兩半。然後,有關這種斬波的資訊必須通過整個管道傳播,以便我們可以在深網運作後重新組裝它。另一個棘手的問題是處理深色背景上帶有白色文本的圖像,而不是白色背景上的深色文本,這迫使我們的 MSER 檢測器能夠處理這兩種情況。

組合式端到端系統

一旦我們将Word Detector改進到可接受的點,我們就将其與Word Deep Net連結在一起,以便我們可以針對文檔級圖像而不是我們較舊的單字準确性基準測試套件對整個組合系統進行端到端的基準測試。然而,當我們第一次測量端到端的準确性時,我們發現我們的表現約為44%, 比競争對手差得多。

主要問題是圖像中噪聲的間距和虛假的垃圾文本。有時我們會錯誤地将兩個單詞組合在一起,例如“helloworld”,或者錯誤地将單個單詞片段化,例如“ wo rld”。

我們的解決方案是修改網絡的連接配接主義時間分類 (CTC) 層,以便除了預測文本之外,還為我們提供置信度分數。然後,我們使用此置信度分數以三種方式對預測進行篩選:

  1. 如果置信度很高,我們保持預測不變。
  2. 如果置信度很低,我們隻是簡單地過濾掉它們,打賭這些是噪聲預測。
  3. 如果置信度在中間的某個地方,然後我們通過牛津英語詞典生成的詞典進行運作,在單詞預測框之間和内部應用不同的轉換,嘗試組合單詞或以各種方式拆分它們以檢視它們是否在詞典中。

我們還必須處理由前面提到的Word Deep Net的固定接受圖像大小引起的問題:即單個“單詞”視窗實際上可能包含多個單詞或僅包含很長單詞的一部分。是以,我們通過一個稱為Wordinator的子產品将這些輸出與來自單詞檢測器的原始輸出一起運作,該子產品為每個單獨的OCRed單詞提供了離散的邊界框。這将生成單個單詞坐标及其 OCRed 文本。

例如,在我們的系統中的以下調試可視化中,您可以在Wordinator之前看到檢測到的單詞周圍的框:

使用計算機視覺深入學習和建立現代OCR管道

Wordinator 會将其中一些框分解為單獨的單詞坐标框,例如“of”和“Engineering”,它們目前是同一框的一部分。

最後,現在我們有了一個完全工作的端到端系統,我們生成了超過一千萬個合成單詞,并訓練我們的神經網絡進行大量的疊代,以盡可能多地提高準确性。所有這些最終為我們提供了準确性、精确度和召回率資料,這些數字都達到或超過了 OCR 最先進的技術。

我們短暫地拍了拍自己的後背,然後開始為下一個艱難的階段做準備:生産。

生産化

在這一點上,我們有一個原型Python和Lua腳本包裝Torch的集合 - 當然還有一個訓練有素的模型!— 這表明我們可以實作最先進的 OCR 準确性。但是,這與實際使用者可以在具有可靠性,性能和可靠工程的分布式環境中使用的系統相去甚遠。我們需要建立一個适合數百萬使用者使用的分布式管道,以及一個取代原型腳本的系統。此外,我們必須在不中斷現有 OCR 系統的情況下使用商用現成 SDK 來做到這一點。

下面是生産化 OCR 管道的圖示:

使用計算機視覺深入學習和建立現代OCR管道

整體生産 OCR 管道

我們首先為不同的OCR引擎建立一個抽象,包括我們自己的引擎和商業引擎,并使用我們的内部實驗架構Stormcrow對其進行了控制。這使我們能夠在不中斷現有 OCR 系統的情況下引入新管道的骨架,該系統已經為數百萬商業客戶在生産中運作。

我們還将基于Torch的模型(包括CTC層)移植到TensorFlow,原因有幾個。首先,我們已經在生産環境中對TensorFlow進行了标準化,以便更輕松地管理模型和部署。其次,我們更喜歡使用Python而不是Lua,TensorFlow具有出色的Python綁定。

在新管道中,移動用戶端将掃描的文檔圖像上傳到我們的内部異步工作隊列。上傳完成後,我們通過遠端過程調用 (RPC) 将圖像發送到運作 OCR 服務的伺服器群集。

實際的OCR服務使用OpenCV和TensorFlow,兩者都是用C++編寫的,并且具有複雜的庫依賴關系;是以,安全漏洞是一個真正的問題。我們使用 LXC、CGroups、Linux Namespaces 和 Seccomp 等技術将實際的 OCR 部分隔離到 jail 中,以提供隔離和系統白名單,使用 IPC 進出隔離的容器。如果有人破壞了監獄,他們仍然會與我們系統的其餘部分完全分離。

我們的 jail 基礎結構允許我們在啟動時一次性有效地設定昂貴的資源,例如加載我們訓練的模型,然後将這些資源克隆到 jail 中以滿足單個 OCR 請求。這些資源被克隆為寫入時複制到分叉的 jail 中,并且對于我們如何使用模型是隻讀的,是以它非常高效和快速。我們必須修補TensorFlow,以便更容易地進行這種分叉。(我們在上遊送出了更新檔。

一旦我們獲得單詞邊界框及其OCRed文本,我們就會将它們合并回移動文檔掃描器生成的原始PDF中,作為OCR隐藏層。是以,使用者将獲得一個包含掃描圖像和檢測到的文本的PDF。OCRed 文本也會添加到 Dropbox 的搜尋索引中。使用者現在可以突出顯示和複制粘貼PDF中的文本,由于我們隐藏的單詞框坐标,突出顯示的位置将放在正确的位置。他們還可以通過 Dropbox 上的 OCRed 文本搜尋掃描的 PDF。

性能調優

在這一點上,我們現在有一個實際的工程管道(帶有單元測試和持續內建!),但仍然存在性能問題。

第一個問題是,我們是否會在推理時在生産中使用 CPU 或 GPU。訓練深度網絡比在推理時使用它需要更長的時間。在訓練期間使用GPU是很常見的(就像我們所做的那樣),因為它們大大減少了訓練深度網絡所需的時間。但是,在推理時使用 GPU 是目前更難進行的調用。

首先,在Dropbox等生産資料中心擁有高端GPU仍然有點異國情調,并且與其他機隊不同。此外,基于 GPU 的機器更加昂貴,并且基于快速開發,配置正在更快地攪動。我們對我們的Word Detector和Word Deep Net在CPU和GPU上的表現進行了廣泛的分析,假設充分利用每個CPU上的所有核心以及CPU的特征。經過大量分析,我們決定,與 GPU 機器相比,我們隻需在 CPU 上即可達到性能目标,成本相似或更低。

一旦我們決定使用CPU,我們就需要針對Word Deep Net優化我們的系統BLAS庫,稍微調整我們的網絡,并将TensorFlow配置為使用可用核心。我們的單詞檢測器也是一個重要的瓶頸。我們最終以更子產品化的方式重寫了OpenCV C++MSER實作,以避免在執行兩次傳遞時重複緩慢的工作(以便能夠處理黑白文本以及黑文本上的白色);向我們的Python層(底層MSER樹層次結構)公開更多内容,以實作更有效的處理;并使代碼實際可讀。我們還必須優化 MSER 後 Word 檢測管道,以調整和矢量化其中的某些慢速部分。

在所有這些工作之後,我們現在有了一個生産化的高性能系統,我們可以為少數使用者“影子打開”,進而進入第三個階段:改進。

優雅

由于我們提議的系統在生産中與商業OCR系統并行運作,我們需要确認我們的系統确實更好,這是根據真實使用者資料測量的。在 Dropbox,我們非常重視使用者資料隐私,是以我們不能隻檢視和測試随機移動文檔掃描圖像。相反,我們使用前面詳述的使用者圖像捐贈流程來獲得評估圖像。然後,我們使用這些捐贈的圖像,非常小心地保護它們的隐私,對兩個OCR系統進行端到端的定性黑盒測試,并且很高興地發現我們确實執行了與舊的商業OCR SDK相同或更好的功能,使我們能夠将我們的系統提升到100%的Dropbox Business使用者。

接下來,我們測試了在這些捐贈文檔上微調我們訓練有素的深度網絡與我們手工選擇的微調圖像套件是否有助于提高準确性。不幸的是,它沒有移動針頭。

另一個重要的改進是進行方向檢測,這是我們在原始管道中沒有完成的。來自移動文檔掃描器的圖像可以旋轉90°甚至倒置。我們使用另一個基于Inception Resnet v2架構的深度網絡建構了一個方向預測器,更改了最後一層以預測方向,收集了方向訓練和驗證資料集,并從偏向于我們自己需求的ImageNet訓練模型中進行了微調。我們将此方向預測器放入管道中,使用其檢測到的方向将圖像旋轉到直立,然後再進行字檢測和 OCRing。

方向預測器的一個棘手方面是,隻有一小部分圖像實際上是旋轉的。我們需要確定我們的系統在嘗試修複較少數量的非直立圖像的方向時,不會無意中旋轉直立圖像(最常見的情況)。此外,我們必須解決各種棘手的問題,将我們的直立旋轉圖像與PDF檔案格式可以應用自己的轉換矩陣進行旋轉的不同方式相結合。

繼續閱讀