มีวิธีง่าย ๆ ในการระบุว่าสแกน PDF หรือไม่?


8

ฉันมีเอกสารหลายพันฉบับและสแกนบางส่วนแล้ว ดังนั้นฉันต้องการสคริปต์เพื่อทดสอบไฟล์ PDF ทั้งหมดที่เป็นของไดเรกทอรี มีวิธีง่าย ๆ ในการทำเช่นนั้น?

  1. PDF ส่วนใหญ่เป็นรายงาน ดังนั้นพวกเขามีข้อความจำนวนมาก
  2. พวกเขาแตกต่างกันมาก แต่คนที่สแกนตามที่ระบุไว้ด้านล่างหนึ่งสามารถค้นหาข้อความเนื่องจากกระบวนการ OCR ล่อแหลมคู่กับการสแกน

  3. ข้อเสนอเนื่องจาก Sudodus ในความคิดเห็นด้านล่างดูเหมือนจะน่าสนใจมาก ดูความแตกต่างระหว่างการสแกนเป็น PDF ที่ไม่ได้สแกน:

สแกน:

grep --color -a 'Image' AR-G1002.pdf
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 340615/Name/Obj13/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40452/Name/Obj18/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41680/Name/Obj23/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 41432/Name/Obj28/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59084/Name/Obj33/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 472681/Name/Obj38/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 469340/Name/Obj43/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 371863/Name/Obj48/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 344092/Name/Obj53/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 59416/Name/Obj58/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 48308/Name/Obj63/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 51564/Name/Obj68/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 63184/Name/Obj73/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 40824/Name/Obj78/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 23320/Name/Obj83/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 31504/Name/Obj93/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 18996/Name/Obj98/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 8/ColorSpace/DeviceRGB/Filter[/DCTDecode]/Height 2197/Length 292932/Name/Obj103/Subtype/Image/Type/XObject/Width 1698>>stream
<</BitsPerComponent 1/ColorSpace/DeviceGray/DecodeParms<</Columns 1698/K -1>>/Filter/CCITTFaxDecode/Height 2197/Length 27720/Name/Obj108/Subtype/Image/Type/XObject/Width 1698>>stream
               <rdf:li xml:lang="x-default">Image</rdf:li>
               <rdf:li xml:lang="x-default">Image</rdf:li>

ไม่สแกน:

grep --color -a 'Image' AR-G1003.pdf
<</Lang(en-US)/MarkInfo<</Marked true>>/Metadata 167 0 R/Pages 2 0 R/StructTreeR<</Contents 4 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F4 11 0 R/F5 13 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/StructParents 0/Tabs/S/Type/<</Filter/FlateDecode/Length 5463>>stream
<</BaseFont/Times#20New#20Roman,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontD<</Ascent 891/AvgWidth 427/CapHeight 677/Descent -216/Flags 32/FontBBox[-558 -216 2000 677]/FontName/Times#20New#20Roman,Bold/FontWeight 700/ItalicAngle 0/Leadi<</BaseFont/Times#20New#20Roman/Encoding/WinAnsiEncoding/FirstChar 32/FontDescri<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontName/Times#20New#20Roman/FontWeight 400/ItalicAngle 0/Leading 42<</BaseFont/Arial,Bold/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 10 0<</Ascent 905/AvgWidth 479/CapHeight 728/Descent -210/Flags 32/FontBBox[-628 -210 2000 728]/FontName/Arial,Bold/FontWeight 700/ItalicAngle 0/Leading 33/MaxWidth<</BaseFont/Times#20New#20Roman,Italic/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 12 0 R/LastChar 118/Name/F4/Subtype/TrueType/Type/Font/Widths 164 0 <</Ascent 891/AvgWidth 402/CapHeight 694/Descent -216/Flags 32/FontBBox[-498 -216 1333 694]/FontName/Times#20New#20Roman,Italic/FontWeight 400/ItalicAngle -16.4<</BaseFont/Arial/Encoding/WinAnsiEncoding/FirstChar 32/FontDescriptor 14 0 R/La<</Ascent 905/AvgWidth 441/CapHeight 728/Descent -210/Flags 32/FontBBox[-665 -210 2000 728]/FontName/Arial/FontWeight 400/ItalicAngle 0/Leading 33/MaxWidth 2665<</Contents 16 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 7534>>streamarents 1/Tabs/S/Type/Page>>
<</Contents 18 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R>>/ProcSet[<</Filter/FlateDecode/Length 6137>>streamarents 2/Tabs/S/Type/Page>>
<</Contents 20 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 7 0 R/F5 13 0 R/F6 21 0 R><</Filter/FlateDecode/Length 6533>>stream>>/StructParents 3/Tabs/S/Type/Page>>
<</BaseFont/Times#20New#20Roman/DescendantFonts 22 0 R/Encoding/Identity-H/Subty<</BaseFont/Times#20New#20Roman/CIDSystemInfo 24 0 R/CIDToGIDMap/Identity/DW 100<</Ascent 891/AvgWidth 401/CapHeight 693/Descent -216/Flags 32/FontBBox[-568 -216 2000 693]/FontFile2 160 0 R/FontName/Times#20New#20Roman/FontWeight 400/Italic<</Contents 27 0 R/Group<</CS/DeviceRGB/S/Transparency/Type/Group>>/MediaBox[0 0 612 792]/Parent 2 0 R/Resources<</ExtGState<</GS28 28 0 R/GS29 29 0 R>>/Font<</F1 5 0 R/F2 7 0 R/F3 9 0 R/F5 13 0 R/F6 21 0 R>>/ProcSet[/PDF/Text/ImageB/ImageC<</Filter/FlateDecode/Length 5369>>streamge>>

จำนวนรูปภาพต่อหน้านั้นใหญ่กว่ามาก (ประมาณหนึ่งต่อหน้า)!


7
คุณหมายถึงว่าพวกเขาเป็นข้อความหรือภาพ?
DK Bose

8
ทำไมคุณถึงอยากรู้ว่าไฟล์ PDF ถูกสแกนหรือไม่? คุณตั้งใจจะใช้ข้อมูลนั้นอย่างไร?
sudodus

4
@ Sudodus ถามคำถามที่ดีมาก ตัวอย่างเช่น PDF ที่สแกนส่วนใหญ่มีข้อความให้เลือกแปลงโดยใช้ OCR คุณสร้างความแตกต่างระหว่างไฟล์และไฟล์ข้อความดังกล่าวหรือไม่? คุณรู้ที่มาของ PDF ของคุณหรือไม่
ท่อ

1
ข้อมูลเมตาของการสแกนและเอกสารที่สแกนต่างกันหรือไม่? นั่นจะเป็นวิธีที่สะอาดและง่ายมาก
ของหวาน

1
หากpdfไฟล์มีภาพ (แทรกในเอกสารพร้อมกับข้อความหรือเป็นหน้าทั้ง 'สแกนไฟล์ PDF) ไฟล์มักจะ (อาจจะเสมอ) มีสตริงซึ่งสามารถพบได้กับบรรทัดคำสั่ง/Image/ grep --color -a 'Image' filename.pdfสิ่งนี้จะแยกไฟล์ที่มีเพียงข้อความจากภาพที่มี (ภาพเต็มหน้ารวมถึงหน้าข้อความที่มีโลโก้ขนาดเล็กและภาพแสดงขนาดกลาง)
sudodus

คำตอบ:


4

Shellscript

  • หากpdfไฟล์มีรูปภาพ (แทรกในเอกสารข้างข้อความหรือเป็นทั้งหน้า 'สแกน PDF') ไฟล์นั้นมักจะมีสตริง/Image/อยู่

  • ในทำนองเดียวกันคุณสามารถค้นหาสตริง/Textเพื่อบอกว่าไฟล์ pdf มีข้อความ (ไม่สแกน)

ฉันสร้าง shellscript pdf-text-or-imageและอาจใช้งานได้กับไฟล์ของคุณ shellscript ค้นหาสตริงข้อความ/Image/และ/Textในpdfไฟล์

#!/bin/bash

echo "shellscript $0"
ls --color --group-directories-first
read -p "Is it OK to use this shellscript in this directory? (y/N) " ans
if [ "$ans" != "y" ]
then
 exit
fi

mkdir -p scanned
mkdir -p text
mkdir -p "s-and-t"

for file in *.pdf
do
 grep -aq '/Image/' "$file"
 if [ $? -eq 0 ]
 then
  image=true
 else
  image=false
 fi
 grep -aq '/Text' "$file"
 if [ $? -eq 0 ]
 then
  text=true
 else
  text=false
 fi


 if $image && $text
 then
  mv "$file" "s-and-t"
 elif $image
 then
  mv "$file" "scanned"
 elif $text
 then
  mv "$file" "text"
 else
  echo "$file undecided"
 fi
done

ทำให้ shellscript สามารถเรียกใช้งานได้

chmod ugo+x pdf-text-or-image

เปลี่ยนไดเร็กทอรีเป็นตำแหน่งที่คุณมีpdfไฟล์และรัน shellscript

ไฟล์ที่ระบุจะถูกย้ายไปยังไดเรกทอรีย่อยดังต่อไปนี้

  • scanned
  • text
  • s-and-t (สำหรับเอกสารที่มีทั้ง [สแกน?] ภาพและเนื้อหาข้อความ)

วัตถุไฟล์ที่ไม่ปรากฏชื่อ 'ยูเอฟโอ' จะยังคงอยู่ในไดเรกทอรีปัจจุบัน

ทดสอบ

ผมทดสอบ shellscript กับสองของไฟล์ของคุณAR-G1002.pdfและAR-G1003.pdfและด้วยตัวเองบางpdfไฟล์ (ที่ผมได้สร้างขึ้นโดยใช้ฟรีสำนักงาน Impress)

$ ./pdf-text-or-image
shellscript ./pdf-text-or-image
s-and-t                                 mkUSB-quick-start-manual-11.pdf    mkUSB-quick-start-manual-nox-11.pdf
scanned                                 mkUSB-quick-start-manual-12-0.pdf  mkUSB-quick-start-manual-nox.pdf
text                                    mkUSB-quick-start-manual-12.pdf    mkUSB-quick-start-manual.pdf
AR-G1002.pdf                            mkUSB-quick-start-manual-74.pdf    OBI-quick-start-manual.pdf
AR-G1003.pdf                            mkUSB-quick-start-manual-75.pdf    oem.pdf
DescriptionoftheOneButtonInstaller.pdf  mkUSB-quick-start-manual-8.pdf     pdf-text-or-image
GrowIt.pdf                              mkUSB-quick-start-manual-9.pdf     pdf-text-or-image0
list-files.pdf                          mkUSB-quick-start-manual-bas.pdf   README.pdf
Is it OK to use this shellscript in this directory? (y/N) y

$ ls -1 *
pdf-text-or-image
pdf-text-or-image0

s-and-t:
DescriptionoftheOneButtonInstaller.pdf
GrowIt.pdf
mkUSB-quick-start-manual-11.pdf
mkUSB-quick-start-manual-12-0.pdf
mkUSB-quick-start-manual-12.pdf
mkUSB-quick-start-manual-8.pdf
mkUSB-quick-start-manual-9.pdf
mkUSB-quick-start-manual.pdf
OBI-quick-start-manual.pdf
README.pdf

scanned:
AR-G1002.pdf

text:
AR-G1003.pdf
list-files.pdf
mkUSB-quick-start-manual-74.pdf
mkUSB-quick-start-manual-75.pdf
mkUSB-quick-start-manual-bas.pdf
mkUSB-quick-start-manual-nox-11.pdf
mkUSB-quick-start-manual-nox.pdf
oem.pdf

ให้เราหวังว่า

  • ไม่มี UFO ในชุดไฟล์ของคุณ
  • การจัดเรียงนั้นถูกต้องเกี่ยวกับข้อความเมื่อเทียบกับการสแกน / ภาพ

แทนที่จะเปลี่ยนเส้นทางไปที่ / dev / null คุณสามารถใช้grep -q
phuclv

1
@phuclv ขอบคุณสำหรับเคล็ดลับ :-) ทำให้เร็วขึ้นเช่นกันโดยเฉพาะอย่างยิ่งกับไฟล์ขนาดใหญ่เพราะgrep -qออกจากสถานะทันทีหากพบการจับคู่ใด ๆ (แทนที่จะค้นหาไฟล์ทั้งหมด)
sudodus

6
  1. วางไฟล์. pdf ทั้งหมดไว้ในโฟลเดอร์เดียว
  2. ไม่มีไฟล์. txt ในโฟลเดอร์นั้น
  3. ในไดเรกทอรีการเปลี่ยนแปลงขั้วไปยังโฟลเดอร์ที่มี cd <path to dir>
  4. สร้างอีกหนึ่งไดเรกทอรีสำหรับไฟล์ที่ไม่ได้สแกน ตัวอย่าง:
mkdir ./x 
for file in *.pdf; do
    if [ $(pdftotext "$file")"x" == "x" ] ; then mv "$file" ./x; fi
rm *.txt
done

ไฟล์สแกน PDF ทั้งหมดจะยังคงอยู่ในโฟลเดอร์และไฟล์อื่น ๆ จะย้ายไปที่โฟลเดอร์อื่น


มันเยี่ยมมาก อย่างไรก็ตามไฟล์นี้ไปที่โฟลเดอร์อื่นและถูกสแกน: drive.google.com/open?id=12xIQdRo_cyTf27Ck6DQKvRyRvlkYEzjl จะเกิดอะไรขึ้น
DanielTheRocketMan

8
PDF ที่สแกนมักจะมีเนื้อหาข้อความ OCRed อยู่เสมอดังนั้นฉันเดาว่าการทดสอบอย่างง่ายจะล้มเหลว ตัวบ่งชี้ที่ดีกว่าอาจเป็นหนึ่งภาพขนาดใหญ่ต่อหน้าโดยไม่คำนึงถึงเนื้อหาข้อความ
Joey

2
ลดลงเนื่องจากข้อบกพร่องที่ชัดเจนมาก: คุณจะทราบได้อย่างไรว่าไฟล์ถูกสแกนหรือไม่ในตอนแรก นั่นคือสิ่งที่ OP ขอให้: วิธีการทดสอบโดยทางโปรแกรมสำหรับสแกนหรือไม่
jamesqf

1
@DanielTheRocketMan เวอร์ชันของไฟล์ PDF น่าจะมีผลกับเครื่องมือที่คุณใช้ในการเลือกข้อความ ผลลัพธ์ของfile pdf-filename.pdfจะผลิตหมายเลขรุ่น ฉันไม่สามารถค้นหาข้อความที่ระบุใน BR-L1411-3.pdf BR-L1411-3.pdf: เอกสาร PDF รุ่น 1.3 แต่สามารถค้นหาข้อความในไฟล์อื่น ๆ ที่คุณให้ไว้ซึ่งเป็นรุ่น 1.5 และ 1.6 และรับการแข่งขันหนึ่งครั้งขึ้นไป ฉันใช้โปรแกรมดูไฟล์ PDF XChange เพื่อค้นหาไฟล์เหล่านี้ แต่มีผลลัพธ์ที่คล้ายกันกับ evince เอกสารเวอร์ชัน 1.3 ไม่ตรงกับสิ่งใดเลย
Elder Geek

1
@DanielTheRocketMan หากเป็นกรณีนี้คุณอาจพบว่าการเรียงลำดับเอกสารตามรุ่นโดยใช้ผลลัพธ์ที่มีfileประโยชน์ในการทำให้โครงการเสร็จสมบูรณ์ แม้ว่าฉันดูเหมือนว่าคนอื่นจะยังไม่ชัดเจนในสิ่งที่คุณพยายามที่จะบรรลุ
Elder Geek

2

ฉันสร้างสคริปต์เพื่อตรวจสอบว่า PDF เป็น OCRd หรือไม่ แนวคิดหลัก: ใน OCRd PDF เป็นข้อความที่มองไม่เห็น

อัลกอริทึมเพื่อทดสอบว่า PDF ที่กำหนด ( f1) เป็น OCRd หรือไม่:

  1. สร้างสำเนาของf1บันทึกเป็นf2
  2. ลบข้อความทั้งหมดใน f2
  3. สร้างภาพ (PNG) สำหรับทุกหน้า (หรือเพียงไม่กี่หน้า) สำหรับf1และf2
  4. f1เป็น OCRd หากภาพทั้งหมดf1และf2เหมือนกัน

https://github.com/jfilter/pdf-scripts/blob/master/is_ocrd_pdf.sh

#!/usr/bin/env bash
set -e
set -x

################################################################################
# Check if a PDF was scanned or created digitally, works on OCRd PDFs
#
# Usage:
#   bash is_scanned_pdf.sh [-p] file
#
#   Exit 0: Yes, file is a scanned PDF
#   Exit 99: No, file was created digitally
#
# Arguments:
#   -p or --pages: pos. integer, only consider first N pages
#
# Please report issues at https://github.com/jfilter/pdf-scripts/issues
#
# GPLv3, Copyright (c) 2020 Johannes Filter
################################################################################

# parse arguments
# h/t https://stackoverflow.com/a/33826763/4028896
max_pages=-1
# skip over positional argument of the file(s), thus -gt 1
while [[ "$#" -gt 1 ]]; do
  case $1 in
  -p | --pages)
    max_pages="$2"
    shift
    ;;
  *)
    echo "Unknown parameter passed: $1"
    exit 1
    ;;
  esac
  shift
done

# increment to make it easier with page numbering
max_pages=$((max_pages++))

command_exists() {
  if ! [ -x $($(command -v $1 &>/dev/null)) ]; then
    echo $(error: $1 is not installed.) >&2
    exit 1
  fi
}

command_exists mutool && command_exists gs && command_exists compare
command_exists pdfinfo

orig=$PWD
num_pages=$(pdfinfo $1 | grep Pages | awk '{print $2}')

echo $num_pages

echo $max_pages

if ((($max_pages > 1) && ($max_pages < $num_pages))); then
  num_pages=$max_pages
fi

cd $(mktemp -d)

for ((i = 1; i <= num_pages; i++)); do
  mkdir -p output/$i && echo $i
done

# important to filter text on output of GS (tmp1), cuz GS alters input PDF...
gs -o tmp1.pdf -sDEVICE=pdfwrite -dLastPage=$num_pages $1 &>/dev/null
gs -o tmp2.pdf -sDEVICE=pdfwrite -dFILTERTEXT tmp1.pdf &>/dev/null
mutool convert -o output/%d/1.png tmp1.pdf 2>/dev/null
mutool convert -o output/%d/2.png tmp2.pdf 2>/dev/null

for ((i = 1; i <= num_pages; i++)); do
  echo $i
  # difference in pixels, if 0 there are the same pictures
  # discard diff image
  if ! compare -metric AE output/$i/1.png output/$i/2.png null: 2>&1; then
    echo " pixels difference, not a scanned PDF, mismatch on page $i"
    exit 99
  fi
done

1

Hobbyist เสนอทางออกที่ดีถ้าเอกสารที่สแกนของคอลเลกชันเอกสารไม่มีข้อความที่เพิ่มด้วยการรู้จำอักขระด้วยแสง (OCR) หากเป็นไปได้คุณอาจต้องการทำสคริปต์บางอย่างที่อ่านเอาต์พุตpdfinfo -metaและตรวจสอบเครื่องมือที่ใช้สร้างไฟล์หรือใช้รูทีน Python ที่ใช้ไลบรารี Python เพื่อตรวจสอบ การค้นหาข้อความด้วยเครื่องมือเช่นstringsนี้จะไม่น่าเชื่อถือเนื่องจากเนื้อหา PDF สามารถบีบอัดได้ และการตรวจสอบเครื่องมือสร้างนั้นไม่ปลอดภัยเช่นกันเนื่องจากหน้า PDF สามารถรวมกันได้ ฉันรวมเอกสารข้อความ PDF เป็นประจำกับภาพที่สแกนเพื่อรวมสิ่งต่าง ๆ เข้าด้วยกัน

ฉันขอโทษที่ฉันไม่สามารถให้คำแนะนำที่เฉพาะเจาะจงได้ เป็นเวลานานแล้วที่ฉันแหย่ที่โครงสร้างภายใน PDF แต่ขึ้นอยู่กับความต้องการที่เข้มงวดของคุณคุณอาจต้องการทราบว่ามันซับซ้อน โชคดี!


2
ฉันพยายามใช้ python ด้วย แต่ก็ไม่สำคัญที่จะรู้ว่า PDF สแกนหรือไม่ ประเด็นก็คือว่าแม้เอกสารที่คุณไม่สามารถเลือกข้อความจะแสดงข้อความบางส่วนเมื่อมันถูกแปลงเป็น txt ตัวอย่างเช่นฉันใช้ pdf miner ใน Python และฉันสามารถค้นหาข้อความบางส่วนในการแปลงแม้สำหรับ pdf ที่เครื่องมือที่เลือกไม่ทำงาน
DanielTheRocketMan

1

หากนี่เป็นเรื่องเกี่ยวกับการตรวจจับว่า PDF ถูกสร้างขึ้นโดยการสแกนมากกว่าpdf มีรูปภาพแทนที่จะเป็นข้อความคุณอาจต้องขุดลงในเมทาดาทาของไฟล์ไม่ใช่แค่เนื้อหา

โดยทั่วไปแล้วสำหรับไฟล์ที่ฉันพบในคอมพิวเตอร์และไฟล์ทดสอบของคุณสิ่งต่อไปนี้เป็นจริง:

  • ไฟล์ที่สแกนมีน้อยกว่า 1,000chars / หน้าเทียบกับที่ไม่ได้สแกนที่มีมากกว่า 1,000chars / หน้า
  • ไฟล์ที่สแกนอิสระหลายไฟล์มี "Canon" ที่ระบุว่าเป็นผู้สร้าง PDF ซึ่งอาจอ้างอิงซอฟต์แวร์สแกนเนอร์ของ Canon
  • PDF ที่มี "Microsoft Word" ในฐานะผู้สร้างมีแนวโน้มที่จะไม่ถูกสแกนเนื่องจากเป็นคำที่ส่งออก แต่บางคนสามารถสแกนคำแล้วส่งออกไปยังไฟล์ PDF - บางคนมีขั้นตอนการทำงานที่แปลกมาก

ฉันใช้ Windows ในขณะนี้ดังนั้นฉันจึงใช้node.jsตัวอย่างต่อไปนี้:

const fs = require("mz/fs");
const pdf_parse = require("pdf-parse");
const path = require("path");


const SHOW_SCANNED_ONES = process.argv.indexOf("scanned") != -1;

const DEBUG = process.argv.indexOf("debug") != -1;
const STRICT = process.argv.indexOf("strict") != -1;

const debug = DEBUG ? console.error : () => { };

(async () => {
    const pdfs = (await fs.readdir(".")).filter((fname) => { return fname.endsWith(".pdf") });

    for (let i = 0, l = pdfs.length; i < l; ++i) {
        const pdffilename = pdfs[i];
        try {
            debug("\n\nFILE: ", pdffilename);
            const buffer = await fs.readFile(pdffilename);
            const data = await pdf_parse(buffer);

            if (!data.info)
                data.indo = {};
            if (!data.metadata) {
                data.metadata = {
                    _metadata: {}
                };
            }


            // PDF info
            debug(data.info);
            // PDF metadata
            debug(data.metadata);
            // text length
            const textLen = data.text ? data.text.length : 0;
            const textPerPage = textLen / (data.numpages);
            debug("Text length: ", textLen);
            debug("Chars per page: ", textLen / data.numpages);
            // PDF.js version
            // check https://mozilla.github.io/pdf.js/getting_started/
            debug(data.version);

            if (evalScanned(data, textLen, textPerPage) == SHOW_SCANNED_ONES) {
                console.log(path.resolve(".", pdffilename));
            }
        }
        catch (e) {
            if (strict && !debug) {
                console.error("Failed to evaluate " + item);
            }
            {
                debug("Failed to evaluate " + item);
                debug(e.stack);
            }
            if (strict) {
                process.exit(1);
            }
        }
    }
})();
const IS_CREATOR_CANON = /canon/i;
const IS_CREATOR_MS_WORD = /microsoft.*?word/i;
// just defined for better clarity or return values
const IS_SCANNED = true;
const IS_NOT_SCANNED = false;
function evalScanned(pdfdata, textLen, textPerPage) {
    if (textPerPage < 300 && pdfdata.numpages>1) {
        // really low number, definitelly not text pdf
        return IS_SCANNED;
    }
    // definitelly has enough text
    // might be scanned but OCRed
    // we return this if no 
    // suspition of scanning is found
    let implicitAssumption = textPerPage > 1000 ? IS_NOT_SCANNED : IS_SCANNED;
    if (IS_CREATOR_CANON.test(pdfdata.info.Creator)) {
        // this is always scanned, canon is brand name
        return IS_SCANNED;
    }
    return implicitAssumption;
}

ในการเรียกใช้คุณต้องติดตั้ง Node.js (ควรเป็นคำสั่งเดียว) และคุณต้องโทรหาด้วย:

npm install mz pdf-parse

การใช้งาน:

node howYouNamedIt.js [scanned] [debug] [strict]

 - scanned show PDFs thought to be scanned (otherwise shows not scanned)
 - debug shows the debug info such as metadata and error stack traces
 - strict kills the program on first error

ตัวอย่างนี้ไม่ถือว่าเป็นโซลูชันที่เสร็จสิ้น แต่ด้วยการdebugตั้งค่าสถานะคุณจะได้รับข้อมูลเชิงลึกเกี่ยวกับข้อมูลเมตาของไฟล์:

FILE:  BR-L1411-3-scanned.pdf
{ PDFFormatVersion: '1.3',
  IsAcroFormPresent: false,
  IsXFAPresent: false,
  Creator: 'Canon ',
  Producer: ' ',
  CreationDate: 'D:20131212150500-03\'00\'',
  ModDate: 'D:20140709104225-03\'00\'' }
Metadata {
  _metadata:
   { 'xmp:createdate': '2013-12-12T15:05-03:00',
     'xmp:creatortool': 'Canon',
     'xmp:modifydate': '2014-07-09T10:42:25-03:00',
     'xmp:metadatadate': '2014-07-09T10:42:25-03:00',
     'pdf:producer': '',
     'xmpmm:documentid': 'uuid:79a14710-88e2-4849-96b1-512e89ee8dab',
     'xmpmm:instanceid': 'uuid:1d2b2106-a13f-48c6-8bca-6795aa955ad1',
     'dc:format': 'application/pdf' } }
Text length:  772
Chars per page:  2
1.10.100
D:\web\so-odpovedi\pdf\BR-L1411-3-scanned.pdf

ฟังก์ชันไร้เดียงสาที่ฉันเขียนมีความสำเร็จ 100% ในเอกสารที่ฉันสามารถหาได้ในคอมพิวเตอร์ของฉัน (รวมถึงตัวอย่างของคุณ) ฉันตั้งชื่อไฟล์ตามสถานะของไฟล์ก่อนเรียกใช้โปรแกรมเพื่อให้สามารถดูว่าผลลัพธ์ถูกต้องหรือไม่

D:\xxxx\pdf>node detect_scanned.js scanned
D:\xxxx\pdf\AR-G1002-scanned.pdf
D:\xxxx\pdf\AR-G1002_scanned.pdf
D:\xxxx\pdf\BR-L1411-3-scanned.pdf
D:\xxxx\pdf\WHO_TRS_696-scanned.pdf

D:\xxxx\pdf>node detect_scanned.js
D:\xxxx\pdf\AR-G1003-not-scanned.pdf
D:\xxxx\pdf\ASEE_-_thermoelectric_paper_-_final-not-scanned.pdf
D:\xxxx\pdf\MULTIMODE ABSORBER-not-scanned.pdf
D:\xxxx\pdf\ReductionofOxideMineralsbyHydrogenPlasma-not-scanned.pdf

คุณสามารถใช้โหมดดีบักพร้อมกับการเขียนโปรแกรมเล็กน้อยเพื่อปรับปรุงผลลัพธ์ของคุณอย่างมากมาย คุณสามารถส่งผ่านผลลัพธ์ของโปรแกรมไปยังโปรแกรมอื่น ๆ มันจะมีหนึ่งพา ธ เต็มต่อบรรทัด


Re "Microsoft Word" ในฐานะผู้สร้างนั่นขึ้นอยู่กับที่มาของเอกสารต้นฉบับ ตัวอย่างเช่นหากพวกเขาเป็นเอกสารทางวิทยาศาสตร์หลายคนถ้าไม่มากที่สุดจะได้รับการสร้างขึ้นโดยบางสิ่งบางอย่างในเครื่องมือ LaTeX
jamesqf

0

2 วิธีที่ฉันนึกได้:

  1. การใช้เครื่องมือข้อความที่เลือก:หากคุณใช้ PDF ที่สแกนไม่สามารถเลือกข้อความได้ แต่จะมีกล่องปรากฏขึ้น คุณสามารถใช้ข้อเท็จจริงนี้เพื่อสร้างสคริปต์ ฉันรู้ใน C ++ QT มีวิธีไม่แน่ใจใน Linux แม้ว่า

  2. ค้นหาคำในไฟล์:ในไฟล์ PDF ที่ไม่ได้สแกนการค้นหาของคุณจะใช้งานได้ แต่ไม่ใช่ในไฟล์สแกน คุณเพียงแค่ต้องค้นหาคำศัพท์ทั่วไปสำหรับ PDF ทั้งหมดหรือฉันอยากจะพูดว่าค้นหาตัวอักษร 'e' ใน PDF ทั้งหมด มันมีการแจกแจงความถี่สูงสุดดังนั้นโอกาสที่คุณจะพบมันในเอกสารทั้งหมดที่มีข้อความ (ยกเว้นgadsby )

เช่น

grep -rnw '/path/to/pdf/' -e 'e'

ใช้เครื่องมือประมวลผลข้อความใด ๆ


1
สแกน PDF ยังสามารถมีตำราเลือกเพราะ OCR ไม่ได้เป็นสิ่งที่แปลกในปัจจุบันและแม้กระทั่งผู้อ่านหลายรูปแบบไฟล์ PDF ฟรีมี OCR คุณลักษณะ
phuclv

@phuclv: แต่ถ้าไฟล์ถูกแปลงเป็นข้อความด้วย OCR จะไม่เป็นไฟล์ "สแกน" อีกต่อไปอย่างน้อยที่สุดฉันก็เข้าใจวัตถุประสงค์ของ OP แล้ว แม้ว่าตอนนี้คุณจะมีไฟล์ pdf 3 ประเภท: text ab initio, ข้อความจาก OCR และ "text" ที่เป็นภาพสแกน
jamesqf

1
@jamesqf โปรดดูตัวอย่างด้านบน พวกเขากำลังสแกน PDF ข้อความส่วนใหญ่ที่ฉันไม่สามารถเรียกคืนได้โดยใช้ pdfminer ทั่วไป
DanielTheRocketMan

1
ฉันคิดว่า op ต้องการ rethink / rephrase คำจำกัดความของการสแกนในกรณีนั้นหรือหยุดการใช้ acrobat x ซึ่งจะสแกนสำเนาและใช้มันในรูปแบบ
ocr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.