Τεχνικό Άρθρο

Μετατόπιση OCR text layer HotPDF: χαρτογράφηση μέσω CropBox

OCR text layers, barcode bounds και boxes redaction προσώπων λοξοδρομούν σε cropped σελίδες PDF όταν τα pixels bitmap χαρτογραφούνται πίσω μέσω του MediaBox αντί για το box που ραστεροποίησε πραγματικά ο renderer: το CropBox κομμένο στο MediaBox (ISO 32000-1 §14.11.2). Το HotPDF το διόρθωσε για το ApplyLoadedOCRTextLayer στο v2.770.153, και για τα DecodeLoadedPageBarcodes και DetectLoadedRedactionFindings στο v2.770.154

Η bug αναφορά που συνήθως φτάνει μοιάζει έτσι. Ένα σαρωμένο αρχείο συμβολαίων περνά από OCR, η έξοδος είναι αναζητήσιμη, και το χτύπημα αναζήτησης για αριθμό άρθρου επισημαίνεται μισή ίντσα πιο κάτω και πιο αριστερά από τον τυπωμένο αριθμό. Τα περισσότερα αρχεία της παρτίδας είναι μια χαρά. Τα χαλασμένα ήρθαν όλα από έναν σταθμό σάρωσης που γράφει /CropBox για να κόψει το περιθώριο του platen. Αυτή η μία λεπτομέρεια χωρίζει την εικόνα που είδε ο OCR engine από το πλαίσιο μέσα στο οποίο τοποθετήθηκε το text layer, και η ίδια ασυμφωνία μετακινεί barcode bounds και, πιο σοβαρά, boxes redaction προσώπων

Γιατί το OCR text layer λοξοδρομεί μακριά από τις σαρωμένες λέξεις;

Το text layer λοξοδρομεί επειδή τα δύο μισά του αγωγού διαφωνούσαν για το ποιο ορθογώνιο καλύπτει το bitmap. Στο v2.766.64, το HotPDF άλλαξε rendering, SVG export, viewer και εκτύπωση να σέβονται το CropBox: μια σελίδα προβάλλεται μέσω του CropBox της κομμένου στο MediaBox της, που είναι ό,τι ορίζει το ISO 32000-1 §14.11.2, και το GetLoadedPageVisibleBox προστέθηκε να επιστρέφει εκείνο το ορατό box. Τα χαρακτηριστικά αναγνώρισης συνέχιζαν να χτίζουν τον μετασχηματισμό τους device-to-page από το GetLoadedPageBox(PageIndex, pbMediaBox, ...). Το raster πλέον κάλυπτε το ορατό box, ο μετασχηματισμός εξακολουθούσε να υποθέτει MediaBox, και κάθε αναγνωρισμένη θέση επέστρεφε μετατοπισμένη κατά το κενό ανάμεσα στα δύο

Το επηρεαζόμενο παράθυρο είναι επομένως ακριβές. Το ApplyLoadedOCRTextLayer τοποθετούσε λάθος κείμενο από το v2.766.64 έως το v2.770.152. Ολόκληρης σελίδας DecodeLoadedPageBarcodes και η ανίχνευση προσώπων μέσα στο DetectLoadedRedactionFindings έμειναν λάθος ένα build ακόμα, έως το v2.770.153. Πριν το v2.766.64 ο renderer σχεδίαζε όλο το MediaBox, οπότε χαρτογράφηση και raster συμφωνούσαν, με το τίμημα της αναγνώρισης περιεχομένου που τα viewers δεν δείχνουν ποτέ. Οι διορθώσεις άλλαξαν τρία πράγματα μαζί για κάθε χαρακτηριστικό: τον μετασχηματισμό, την εκτίμηση του pixel προϋπολογισμού, και το page box που παραδίδεται σε custom engine στο request record

Μερικές περιπτώσεις δεν επηρεάστηκαν ποτέ:

  • Σελίδες χωρίς /CropBox, ή των οποίων το CropBox ισούται με το MediaBox, χαρτογραφούνται πανομοιότυπα πριν και μετά τη διόρθωση
  • Το DecodeLoadedPageBarcodes με ορισμένο HasRegion ραστεροποιεί ακριβώς το region που περνάτε και χαρτογραφεί μέσω εκείνου του ίδιου region, οπότε η αποκωδικοποίηση με ρητό region ήταν σωστή διαρκώς· ο έλεγχος ότι το region βρίσκεται μέσα στη σελίδα εξακολουθεί να χρησιμοποιεί το MediaBox
  • Τα findings redaction βάσει pattern (emails, αριθμοί καρτών και λοιπά) προέρχονται από εξαγωγή κειμένου σε user space, όχι από raster, οπότε μετακινήθηκαν μόνο τα findings ανίχνευσης προσώπων

Τρία συστήματα συντεταγμένων, και ποια APIs HotPDF χρησιμοποιεί το καθένα

Κώδικας HotPDF που αγγίζει αναγνώριση δουλεύει με τρία συστήματα, και τα περισσότερα bugs χαρτογράφησης προέρχονται από την ανάμειξη δύο από αυτά

  • Pixels bitmap: αρχή πάνω αριστερά, το Y μεγαλώνει προς τα κάτω, μονάδες είναι pixels στο DPI του αιτήματος. Τα THPDFOCRWord.Left, Top, Right και Bottom βρίσκονται σε αυτό το σύστημα, όπως και τα προαιρετικά σημεία baseline, τα αποτελέσματα που επιστρέφει ένα custom IHPDFBarcodeDecoder, και τα boxes από ένα custom IHPDFFaceDetector
  • PDF user space φορτωμένης σελίδας: αρχή κάτω αριστερά, το Y μεγαλώνει προς τα πάνω, μονάδες points, με Bottom < Top. Τα GetLoadedPageBox και GetLoadedPageVisibleBox επιστρέφουν Left, Bottom, Right, Top σε αυτό το σύστημα, όπως και τα πεδία PageLeft, PageBottom, PageRight και PageTop του THPDFOCRRequest, τα όρια στο THPDFDecodedBarcode και τα ορθογώνια στο THPDFRedactionFinding
  • Συντεταγμένες σχεδίασης σελίδας HotPDF: το API που χρησιμοποιείτε για να χτίσετε νέες σελίδες (έξοδος κειμένου, σχήματα, barcodes, links, form fields) δουλεύει με αρχή πάνω αριστερά και Y που μεγαλώνει προς τα κάτω. Εκείνο το σύστημα ανήκει στη δημιουργία εγγράφων και δεν έχει καμία σχέση με τα APIs φορτωμένου εγγράφου παραπάνω, οπότε μην ταΐσετε ποτέ ορθογώνιο user space φορτωμένης σελίδας μέσα του αμετάβλητο

Η εγγραφή λέξης OCR είναι σκόπιμα βασισμένη σε pixels: ένα engine αναφέρει ό,τι είδε στην εικόνα, και το ApplyLoadedOCRTextLayer κατέχει τη μετατροπή. Εκείνη η διαίρεση δουλεύει μόνο όταν η μετατροπή χρησιμοποιεί το σωστό box, που είναι ό,τι επανέφερε το v2.770.153

Συστήματα συντεταγμένων αναγνώρισης HotPDF: pixels bitmap με αρχή πάνω αριστερά που χρησιμοποιούν τα boxes THPDFOCRWord και custom decoders, PDF user space με αρχή κάτω αριστερά που επιστρέφουν τα GetLoadedPageBox και GetLoadedPageVisibleBox, και το API σχεδίασης σελίδας πάνω αριστερά που δεν πρέπει ποτέ να λάβει ορθογώνιο φορτωμένης σελίδας αμετάβλητο
τα engines αναφέρουν pixels επειδή εκείνα είδαν, το HotPDF τα χαρτογραφεί, και η ανάμειξη των δύο συστημάτων είναι ο τρόπος που λοξοδρομούν layers και boxes redaction

Ο μετασχηματισμός device-to-page πίσω από OCR, barcodes και πρόσωπα

Το HotPDF χαρτογραφεί pixels bitmap στη σελίδα με έναν μοναδικό affine πίνακα χτισμένο από πέντε εισόδους: την περιστροφή, την κλίμακα DPI / 72, το ύψος bitmap, και τα Left, Bottom, Right και Top του αποδιδομένου box. OCR, αποκωδικοποίηση barcode και ανίχνευση προσώπων μοιράζονται όλα μία ρουτίνα γι αυτό, γι αυτό ένα λάθος box είσοδος έσπασε και τα τρία με τον ίδιο τρόπο. Για μη στραμμένη σελίδα ο πίνακας page-to-device [A B C D E F] είναι:

  • A = Scale και D = -Scale, όπου Scale = DPI / 72· το αρνητικό D αναποδογυρίζει το user space (Y πάνω) σε bitmap space (Y κάτω)
  • B = C = 0, επειδή μη στραμμένη σελίδα δεν έχει shear ούτε εναλλαγή αξόνων
  • E = -Left * Scale, που μετακινεί το αριστερό άκρο του box στη στήλη pixel 0
  • F = BitmapHeight + Bottom * Scale, που χαρτογραφεί το κάτω άκρο του box σε y = BitmapHeight, το κάτω άκρο του bitmap, ώστε το πάνω άκρο να προσγειώνεται στη γραμμή 0

Τα pixels γυρνάνε στη σελίδα μέσω του αντιστρόφου εκείνου του πίνακα. Το Request.PageRotation κουβαλά το /Rotate της σελίδας κανονικοποιημένο σε 0, 90, 180 ή 270 (καμία τιμή που δεν είναι πολλαπλάσιο του 90 δεν μεταχειρίζεται ως 0), και ο renderer γυρνά τη σελίδα δεξιόστροφα όπως απαιτεί το ISO 32000-1 §7.7.3.3. Υπό περιστροφή οι άξονες εναλλάσσονται και διαφορετικό ζεύγος ακμών box καρφώνεται στην αρχή του bitmap. Γραμμένο ως αντιστροφικοί τύποι, με S = DPI / 72, x και y σε pixels και H το ύψος bitmap:

/RotateX σελίδαςY σελίδαςΑκμές box από τις οποίες εξαρτάται η χαρτογράφηση
0Left + x / SBottom + (H - y) / SLeft, Bottom
90Left + y / SBottom + x / SLeft, Bottom
180Right - x / SBottom + y / SRight, Bottom
270Right - y / STop - x / SRight, Top

Η τελευταία στήλη εξηγεί γιατί το bug φαινόταν τυχαίο στην παραγωγή. Ένα CropBox που κόβει μόνο την κορυφή της σελίδας αφήνει τα Left και Bottom ανέγγιχτα, οπότε όρθιες σελίδες βγαίνανε άψογα και μόνο σελίδες με /Rotate 270 λοξοδρομούσαν. Η περιστροφή εναλλάσσει επίσης τις διαστάσεις του bitmap: στα 90 και 270 το bitmap είναι (Top - Bottom) * S pixels φαρδύ και (Right - Left) * S pixels ψηλό

Πίνακας αντιστροφής χαρτογράφησης HotPDF για περιστροφή σελίδας: σε /Rotate 0 και 90 ο μετασχηματισμός καρφώνει τις ακμές Left και Bottom του αποδιδομένου box, σε 180 καρφώνει Right και Bottom, σε 270 Right και Top, που είναι ο λόγος ένα cropped σελίδα λοξοδρομεί σε διαφορετική κατεύθυνση για κάθε προσανατολισμό σε μικτό έγγραφο
το ίδιο crop μισής ίντσας μοιάζει τρία διαφορετικά bugs μόλις οι σελίδες κουβαλάνε διαφορετικές τιμές /Rotate, επειδή κάθε προσανατολισμός καρφώνει διαφορετικό ζεύγος ακμών box

Τι πάει στραβά με MediaBox [0 0 612 792] και CropBox [36 36 576 756]?

Με crop μισής ίντσας σε κάθε πλευρά, το text layer μιας μη στραμμένης σελίδας προσγειώνεται ακριβώς 36 points αριστερά και 36 points πιο κάτω από τις σαρωμένες λέξεις όταν χρησιμοποιείται το MediaBox. Πάρτε σελίδα US Letter του οποίου το CropBox κόβει 36 points (0.5 ίντσας) από κάθε άκρο. Το ορατό box είναι 540 επί 720 points, οπότε στην προεπιλεγμένη ανάλυση OCR των 300 DPI η κλίμακα είναι 300 / 72 ≈ 4.1667 και το bitmap είναι 2250 επί 3000 pixels

Ας υποθέσουμε ότι το engine αναφέρει λέξη με pixel box Left 450, Top 600, Right 900, Bottom 660 και χωρίς baseline. Το HotPDF τοποθετεί τότε το baseline 20 τοις εκατό του ύψους της λέξης πάνω από το κάτω άκρο, στη γραμμή pixel 648, και χαρτογραφεί το σημείο εκκίνησης (450, 648):

  • Μέσω του ορατού box: x = 36 + 450 / 4.1667 = 144.0 και y = 36 + (3000 - 648) / 4.1667 = 600.48, που είναι εκεί όπου τυπώνεται η λέξη
  • Μέσω του MediaBox: x = 0 + 108.0 = 108.0 και y = 0 + 564.48 = 564.48, μια ομοιόμορφη μετατόπιση (-36, -36) points
Ανατομία drift CropBox HotPDF σε σελίδα US Letter με MediaBox 0 0 612 792 και CropBox 36 36 576 756: ο renderer ραστεροποιεί το ορατό box στα 300 DPI, οπότε χαρτογράφηση pixel λέξης 450 μέσω GetLoadedPageVisibleBox δίνει 144.0 και 600.48 ενώ ο μετασχηματισμός MediaBox προσγειώνεται σε 108.0 και 564.48
το raster καλύπτει το CropBox, οπότε οποιοσδήποτε μετασχηματισμός χτισμένος από το MediaBox μετατοπίζει κάθε αναγνωρισμένη λέξη ακριβώς κατά το περιθώριο του crop

Γυρίστε την ίδια σελίδα και η κατεύθυνση του σφάλματος αλλάζει, επειδή εμπλέκονται διαφορετικές ακμές. Σε /Rotate 180 ο όρος X χρησιμοποιεί το Right, και 612 αντί για 576 σπρώχνει το layer 36 points προς τα δεξιά ενώ το Bottom εξακολουθεί να το τραβάει 36 points κάτω. Σε /Rotate 270 και τα Right και Top είναι πολύ μεγάλα, οπότε το layer μετακινείται 36 points δεξιά και 36 points πάνω. Ένα έγγραφο με μικτούς προσανατολισμούς μπορεί να δείξει το drift σε τρεις κατευθύνσεις, αξιόπιστο fingerprint για αυτό το bug. Χειρόγραφος κώδικας που παράγει την κλίμακα από το box, όπως Bitmap.Width / (Right - Left), τεντώνει επίσης κάθε συντεταγμένη κατά 612 / 540, περίπου 13 τοις εκατό, πάνω στη μετατόπιση

Ποια από τα PDF έγγραφά σας επηρεάζονται;

Ένα PDF έγγραφο εκτίθεται όταν τουλάχιστον μία σελίδα έχει ορατό box που διαφέρει από το MediaBox της, και το HotPDF μπορεί να σας το πει σε λίγες γραμμές. Συγκρίνετε το GetLoadedPageBox με pbMediaBox απέναντι στο GetLoadedPageVisibleBox για κάθε σελίδα, και τυπώστε το GetLoadedPageRotation δίπλα ώστε να προβλέψετε την κατεύθυνση drift από τον πίνακα παραπάνω. Το THPDFPageBoundary προσφέρει επίσης pbCropBox, pbBleedBox, pbTrimBox και pbArtBox, αλλά το GetLoadedPageBox(pbCropBox) υποχωρεί στο MediaBox όταν δεν υπάρχει crop box και δεν κόβει, οπότε το ορατό box είναι το σωστό πράγμα για σύγκριση

uses
  System.SysUtils, HPDFDoc;

procedure ReportCroppedPages(const FileName: string);
var
  Pdf: THotPDF;
  I: Integer;
  ML, MB, MR, MT, VL, VB, VR, VT, Tmp: Single;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile(FileName) < 1 then
      raise Exception.Create('Cannot load ' + FileName);
    for I := 0 to Pdf.LoadedPageCount - 1 do
    begin
      if not Pdf.GetLoadedPageBox(I, pbMediaBox, ML, MB, MR, MT) then
        Continue;
      // ο αποθηκευμένος πίνακας μπορεί να απαριθμεί τις γωνίες του σε οποιαδήποτε σειρά
      if MR < ML then begin Tmp := ML; ML := MR; MR := Tmp; end;
      if MT < MB then begin Tmp := MB; MB := MT; MT := Tmp; end;
      // ήδη κανονικοποιημένο και κομμένο στο MediaBox
      if not Pdf.GetLoadedPageVisibleBox(I, VL, VB, VR, VT) then
        Continue;
      if (Abs(VL - ML) > 0.01) or (Abs(VB - MB) > 0.01) or
         (Abs(VR - MR) > 0.01) or (Abs(VT - MT) > 0.01) then
        Writeln(Format('Page %d  MediaBox [%g %g %g %g]  visible [%g %g %g %g]  /Rotate %d',
          [I + 1, ML, MB, MR, MT, VL, VB, VR, VT,
           Pdf.GetLoadedPageRotation(I)]));
    end;
  finally
    Pdf.Free;
  end;
end;

Δύο λεπτομέρειες του GetLoadedPageVisibleBox μετράνε για scripts σαν αυτό. Η συνάρτηση αφήνει τις out παραμέτρους της ανέγγιχτες όταν αποτυγχάνει, οπότε το προκαθορισμός προεπιλεγμένου μεγέθους σελίδας πριν την κλήση είναι ασφαλές μοτίβο. Και όταν ένα κακοδιατυπωμένο CropBox δεν τέμνει καθόλου το MediaBox, η συνάρτηση επιστρέφει το MediaBox αντί για κενό ορθογώνιο. Αν η αναφορά απαριθμεί σελίδες και το αναπτυγμένο build σας είναι παλαιότερο από v2.770.153 για OCR, ή v2.770.154 για barcodes και πρόσωπα, ξανατρέξτε αναγνώριση σε εκείνες τις σελίδες μετά την αναβάθμιση. Ένα OCR layer που δέσμευσε επηρεαζόμενο build μένει στο αποθηκευμένο αρχείο, και η προεπιλογή SkipPagesWithText θα παραλείψει εκείνες τις σελίδες σε δεύτερο πέρασμα εκτός αν το σβήσετε ή αφαιρέσετε πρώτα το παλιό layer

Πώς πρέπει ένα custom IHPDFOCREngine να χαρτογραφεί pixels πίσω σε PDF space;

Ένα custom IHPDFOCREngine πρέπει να επιστρέφει word boxes σε pixels bitmap και να αφήνει το HotPDF να κάνει τη χαρτογράφηση· μετατρέψτε σε user space μόνο για δικές σας αποφάσεις, και τότε χρησιμοποιήστε το box από το αίτημα, ποτέ το MediaBox. Από το v2.770.153 τα PageLeft, PageBottom, PageRight και PageTop του αιτήματος περιγράφουν το αποδιδομένο ορατό box, οπότε ταιριάζουν ακριβώς το Request.Bitmap. Ο βοηθός παρακάτω είναι ο αντίστροφος του μετασχηματισμού της βιβλιοθήκης, συμπεριλαμβανομένης της χρήσης του πραγματικού ύψους bitmap για όρθιες σελίδες, οπότε συμφωνεί με το HotPDF στο pixel

uses
  System.SysUtils, System.Math, Vcl.Graphics, HPDFDoc;

// Pixel bitmap (αρχή πάνω αριστερά, Y κάτω) σε PDF user space
// (αρχή κάτω αριστερά, Y πάνω), μέσω του box από το οποίο ραστεροποιήθηκε το bitmap
procedure HotPixelToPage(Rotation, DPI, BitmapHeight: Integer;
  Left, Bottom, Right, Top: Single; X, Y: Double;
  out PageX, PageY: Double);
var
  S: Double;
begin
  S := DPI / 72.0;
  case Rotation of
    90:  begin PageX := Left + Y / S;  PageY := Bottom + X / S; end;
    180: begin PageX := Right - X / S; PageY := Bottom + Y / S; end;
    270: begin PageX := Right - Y / S; PageY := Top - X / S; end;
  else
    PageX := Left + X / S;
    PageY := Bottom + (BitmapHeight - Y) / S;
  end;
end;

Ένας ρεαλιστικός λόγος να χρειάζεστε user space μέσα σε engine είναι κανόνας ζώνης: invoices των οποίων το letterhead δεν θέλετε ποτέ αναζητήσιμο, ή περιοχή σφραγίδας που μπερδεύει τον recognizer. Το engine παρακάτω, γραμμένο με TInterfacedObject ώστε το reference counting να χειρίζεται τη διάρκεια ζωής του, φιλτράρει λέξεις από το πού πέφτουν τα κέντρα τους στη σελίδα, και μετά επιστρέφει τους επιζώντες ανέγγιχτους σε συντεταγμένες pixel. Το RunRecognizer αντικαθιστά τη δική σας κλήση recognizer

type
  TZoneFilterOCREngine = class(TInterfacedObject, IHPDFOCREngine)
  private
    FSkipLeft, FSkipBottom, FSkipRight, FSkipTop: Single;  // user space
    function RunRecognizer(Bitmap: TBitmap; MaxWords: Integer;
      out Words: THPDFOCRWords): boolean;  // ο recognizer σας, pixel boxes
  public
    constructor Create(SkipLeft, SkipBottom, SkipRight, SkipTop: Single);
    function GetName: AnsiString;
    function Recognize(const Request: THPDFOCRRequest;
      out Words: THPDFOCRWords; out Diagnostic: AnsiString): boolean;
  end;

function TZoneFilterOCREngine.Recognize(const Request: THPDFOCRRequest;
  out Words: THPDFOCRWords; out Diagnostic: AnsiString): boolean;
var
  Raw: THPDFOCRWords;
  I, Count: Integer;
  CX, CY: Double;
begin
  Diagnostic := '';
  SetLength(Words, 0);
  if not RunRecognizer(Request.Bitmap, Request.MaxWords, Raw) then
  begin
    Diagnostic := 'recognizer failed';
    Exit(False);
  end;
  SetLength(Words, Length(Raw));
  Count := 0;
  for I := 0 to High(Raw) do
  begin
    HotPixelToPage(Request.PageRotation, Request.DPI,
      Request.Bitmap.Height, Request.PageLeft, Request.PageBottom,
      Request.PageRight, Request.PageTop,
      (Raw[I].Left + Raw[I].Right) / 2, (Raw[I].Top + Raw[I].Bottom) / 2,
      CX, CY);
    if (CX >= FSkipLeft) and (CX <= FSkipRight) and
       (CY >= FSkipBottom) and (CY <= FSkipTop) then
      Continue;
    Words[Count] := Raw[I];  // ακόμα pixels: το HotPDF τα χαρτογραφεί μόνο του
    Inc(Count);
  end;
  SetLength(Words, Count);
  Result := True;
end;

Παραδώστε το engine στο ApplyLoadedOCRTextLayer(PageIndices, Engine, Options, Info) όπως με κάθε άλλο engine. Η βιβλιοθήκη επικυρώνει ό,τι επιστρέφεται πριν το εμπιστευτεί: μια λέξη απορρίπτεται και μετράται στο Info.DroppedWordCount όταν το box της φεύγει από το bitmap, όταν Right <= Left ή Bottom <= Top, ή όταν το Confidence βρίσκεται έξω από 0..1 ή κάτω από MinimumConfidence. Η επιστροφή περισσότερων λέξεων από MaxWordsPerPage, ή το σπρώξιμο του τρεχούμενου συνόλου πάνω από MaxTotalWords, αποτυγχάνει όλη την κλήση με σφάλμα προϋπολογισμού, οπότε σέβεστε το Request.MaxWords στο engine. Μην μετατρέπετε word boxes σε user space πριν τα επιστρέψετε· το HotPDF θα μεταχειρίζονταν τις τιμές points ως pixels και το layer θα κατέρρεε προς την αρχή του bitmap

Χαρτογράφηση της δικής σας εξόδου detector

Ο ίδιος βοηθός εξυπηρετεί μια σπιτική ροή χτισμένη πάνω στο RenderLoadedPageToBitmap, που αποδίδει το ορατό box και εφαρμόζει /Rotate ακριβώς όπως τα χαρακτηριστικά αναγνώρισης. Διαβάστε το box με GetLoadedPageVisibleBox, κανονικοποιήστε την περιστροφή όπως κάνει το HotPDF, και χαρτογραφήστε δύο opposite γωνίες κάθε pixel box. Ο άξονας Y αναποδογυρίζει και, στα 90 και 270 μοίρες, οι άξονες εναλλάσσονται, οπότε οι χαρτογραφημένες γωνίες βγαίνουν σε καμία σταθερή σειρά· πάρτε το ελάχιστο και μέγιστο των χαρτογραφημένων σημείων, που είναι και ο τρόπος που το HotPDF χτίζει barcode bounds

const
  DPI = 200;
var
  Pdf: THotPDF;
  Bmp: TBitmap;
  VL, VB, VR, VT: Single;
  Rotation: Integer;
  PxL, PxT, PxR, PxB, X1, Y1, X2, Y2: Double;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('scanned-ids.pdf');
    if not Pdf.GetLoadedPageVisibleBox(0, VL, VB, VR, VT) then Exit;
    Rotation := Pdf.GetLoadedPageRotation(0) mod 360;
    if Rotation < 0 then Inc(Rotation, 360);
    if (Rotation <> 90) and (Rotation <> 180) and (Rotation <> 270) then
      Rotation := 0;
    Bmp := Pdf.RenderLoadedPageToBitmap(0, DPI);
    if Bmp = nil then Exit;
    try
      MyDetector(Bmp, PxL, PxT, PxR, PxB);  // ο κώδικάς σας, pixel box
      HotPixelToPage(Rotation, DPI, Bmp.Height, VL, VB, VR, VT,
        PxL, PxT, X1, Y1);
      HotPixelToPage(Rotation, DPI, Bmp.Height, VL, VB, VR, VT,
        PxR, PxB, X2, Y2);
      Writeln(Format('User-space box [%.2f %.2f %.2f %.2f]',
        [Min(X1, X2), Min(Y1, Y2), Max(X1, X2), Max(Y1, Y2)]));
    finally
      Bmp.Free;
    end;
  finally
    Pdf.Free;
  end;
end;

Η συμπεριφορά περιστροφής καλύπτεται πιο βαθιά στο ισοπέδωση περιστροφής σελίδας χωρίς να σπάσουν τα page boxes, και ο αγωγός αποκωδικοποίησης barcode που καταναλώνει τον ίδιο μετασχηματισμό στο αποκωδικοποίηση στραμμένων QR codes από σελίδες PDF. Αν το engine σας τυλίγει external recognizer, το adapter Tesseract OCR για αναζητήσιμο PDF δείχνει την πλευρά απομόνωσης διεργασίας και ακύρωσης της ίδιας διεπαφής

Σύντομη αναφορά: χαρτογράφηση συντεταγμένων ασφαλής για CropBox

  • Ο renderer ραστεροποιεί το ορατό box, το CropBox κομμένο στο MediaBox (ISO 32000-1 §14.11.2)· κάθε χαρτογράφηση pixel-σε-σελίδα πρέπει να χρησιμοποιεί εκείνο το box, διαβασμένο με GetLoadedPageVisibleBox
  • Το HotPDF v2.770.153 διόρθωσε το ApplyLoadedOCRTextLayer· το v2.770.154 διόρθωσε ολόκληρης σελίδας DecodeLoadedPageBarcodes και face findings από DetectLoadedRedactionFindings· builds από το v2.766.64 έως εκείνες τις εκδόσεις επηρεάζονται
  • Τα boxes THPDFOCRWord είναι pixels bitmap με αρχή πάνω αριστερά· τα GetLoadedPageBox και GetLoadedPageVisibleBox επιστρέφουν PDF user space με αρχή κάτω αριστερά και Bottom < Top
  • Η κλίμακα είναι DPI / 72· παράγετέ την από το DPI, ποτέ από page box διαιρεμένο στο πλάτος bitmap
  • Το /Rotate αποφασίζει ποιες ακμές μετράνε: Left και Bottom στα 0 και 90, Right και Bottom στα 180, Right και Top στα 270
  • Επιστρέψτε OCR λέξεις σε pixels και αφήστε το HotPDF να τις χαρτογραφήσει· μετατρέψτε μόνο για τη δική σας λογική φιλτραρίσματος
  • Ξανατρέξτε OCR σε cropped σελίδες που επεξεργάστηκε επηρεαζόμενο build, και θυμηθείτε ότι το SkipPagesWithText παραλείπει σελίδες που κουβαλούν ήδη το παλιό layer

Τα χαρακτηριστικά αναγνώρισης, τα queries page-box και η απόδοση φορτωμένου εγγράφου που χρησιμοποιήθηκαν εδώ έρχονται όλα στο HotPDF component για Delphi και C++Builder· αδειοδότηση, δοκιμαστικές λήψεις και η πλήρης λίστα χαρακτηριστικών βρίσκονται στη σελίδα HotPDF Delphi PDF component