9

我有一个 OCR PDF 文件的小 bash 脚本(稍微修改了这个脚本)。每个文件的基本流程是:

对于 pdf 文件中的每一页:

  1. 将页面转换为 TIFF 图像(imegamagick)
  2. OCR 图像(正方体)
  3. 猫结果到文本文件

脚本:

FILES=/home/tgr/OCR/input/*.pdf
for f in $FILES
do

  FILENAME=$(basename "$f") 
  ENDPAGE=$(pdfinfo $f | grep "^Pages: *[0-9]\+$" | sed 's/.* //')
  OUTPUT="/home/tgr/OCR/output/${FILENAME%.*}.txt"
  RESOLUTION=1400
  touch $OUTPUT
  for i in `seq 1 $ENDPAGE`; do
      convert -monochrome -density $RESOLUTION $f\[$(($i - 1 ))\] page.tif
      echo processing file $f, page $i
      tesseract page.tif tempoutput -l ces
      cat tempoutput.txt >> $OUTPUT
  done

  rm tempoutput.txt
  rm page.tif
done

由于高分辨率和 tesseract 只能使用一个核心的事实,该过程非常缓慢(转换一个 PDF 文件大约需要 3 分钟)。

因为我有数千个 PDF 文件,我想我可以parallel使用所有 4 个内核,但我不知道如何使用它。在我看到的例子中:

Nested for-loops like this:

  (for x in `cat xlist` ; do
    for y in `cat ylist` ; do
      do_something $x $y
    done
  done) | process_output
can be written like this:

parallel do_something {1} {2} :::: xlist ylist | process_output

不幸的是,我无法弄清楚如何应用它。如何并行化我的脚本?

4

2 回答 2

7

由于您有 1000 多个 PDF 文件,因此并行处理 PDF 文件而不是并行处理单个文件中的页面可能就足够了。

function convert_func {
  f=$1
  FILENAME=$(basename "$f") 
  ENDPAGE=$(pdfinfo $f | grep "^Pages: *[0-9]\+$" | sed 's/.* //')
  OUTPUT="/home/tgr/OCR/output/${FILENAME%.*}.txt"
  RESOLUTION=1400
  touch $OUTPUT
  for i in `seq 1 $ENDPAGE`; do
      convert -monochrome -density $RESOLUTION $f\[$(($i - 1 ))\] $$.tif
      echo processing file $f, page $i
      tesseract $$.tif $$ -l ces
      cat $$.txt >> $OUTPUT
  done

  rm $$.txt
  rm $$.tif
}

export -f convert_func

parallel convert_func ::: /home/tgr/OCR/input/*.pdf

观看介绍视频以进行快速介绍: https ://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

浏览本教程(man parallel_tutorial 或http://www.gnu.org/software/parallel/parallel_tutorial.html)。你爱你的命令行。

阅读示例 ( LESS=+/EXAMPLE: man parallel)。

于 2013-09-20T23:11:35.067 回答
2

你可以有这样的脚本。

#!/bin/bash

function convert_func {
    local FILE=$1 RESOLUTION=$2 PAGE_INDEX=$3 OUTPUT=$4
    local TEMP0=$(exec mktemp --suffix ".00.$PAGE_INDEX.tif")
    local TEMP1=$(exec mktemp --suffix ".01.$PAGE_INDEX")
    echo convert -monochrome -density "$RESOLUTION" "${FILE}[$(( PAGE_INDEX - 1 ))]" "$TEMP0"  ## Just for debugging purposes.
    convert -monochrome -density "$RESOLUTION" "${FILE}[$(( PAGE_INDEX - 1 ))]" "$TEMP0"
    echo "processing file $FILE, page $PAGE_INDEX"  ## I think you mean to place this before the line above.
    tesseract "$TEMP0" "$TEMP1" -l ces
    cat "$TEMP1".txt >> "$OUTPUT"  ## Lines may be mixed up from different processes here and a workaround may still be needed but it may no longer be necessary if outputs are small enough.
    rm -f "$TEMP0" "$TEMP1"
}

export -f convert_func

FILES=(/home/tgr/OCR/input/*.pdf)

for F in "${FILES[@]}"; do
    FILENAME=${F##*/}
    ENDPAGE=$(exec pdfinfo "$F" | grep '^Pages: *[0-9]\+$' | sed 's/.* //')
    OUTPUT="/home/tgr/OCR/output/${FILENAME%.*}.txt"
    RESOLUTION=1400
    touch "$OUTPUT"  ## This may no longer be necessary. Or probably you mean to truncate it instead e.g. : > "$OUTPUT"

    for (( I = 1; I <= ENDPAGE; ++I )); do
        printf "%s\xFF%s\xFF%s\xFF%s\x00" "$F" "$RESOLUTION" "$I" "$OUTPUT"
    done | parallel -0 -C $'\xFF' -j 4 -- convert_func '{1}' '{2}' '{3}' '{4}'
done

它导出一个可通过 导入的函数,parallel对参数进行适当的清理,并使用唯一的临时文件来实现并行处理。

更新。这将首先保留多个临时文件的输出,然后将它们连接到一个主输出文件。

#!/bin/bash

shopt -s nullglob

function convert_func {
    local FILE=$1 RESOLUTION=$2 PAGE_INDEX=$3 OUTPUT=$4 TEMPLISTFILE=$5

    local TEMP_TIF=$(exec mktemp --suffix ".01.$PAGE_INDEX.tif")
    local TEMP_TXT_BASE=$(exec mktemp --suffix ".02.$PAGE_INDEX")

    echo "processing file $FILE, page $PAGE_INDEX"

    echo convert -monochrome -density "$RESOLUTION" "${FILE}[$(( PAGE_INDEX - 1 ))]" "$TEMP_TIF"  ## Just for debugging purposes.
    convert -monochrome -density "$RESOLUTION" "${FILE}[$(( PAGE_INDEX - 1 ))]" "$TEMP_TXT_BASE"

    tesseract "$TEMP_TIF" "$TEMP_TXT_BASE" -l ces

    echo "$PAGE_INDEX"$'\t'"${TEMP_TXT_BASE}.txt" >> "$TEMPLISTFILE"

    rm -f "$TEMP_TIF"
}

export -f convert_func

FILES=(/home/tgr/OCR/input/*.pdf)

for F in "${FILES[@]}"; do
    FILENAME=${F##*/}
    ENDPAGE=$(exec pdfinfo "$F" | grep '^Pages: *[0-9]\+$' | sed 's/.* //')
    BASENAME=${FILENAME%.*}
    OUTPUT="/home/tgr/OCR/output/$BASENAME.txt"
    RESOLUTION=1400

    TEMPLISTFILE=$(exec mktemp --suffix ".00.$BASENAME")
    : > "$TEMPLISTFILE"

    for (( I = 1; I <= ENDPAGE; ++I )); do
        printf "%s\xFF%s\xFF%s\xFF%s\x00" "$F" "$RESOLUTION" "$I" "$OUTPUT"
    done | parallel -0 -C $'\xFF' -j 4 -- convert_func '{1}' '{2}' '{3}' '{4}' "$TEMPLISTFILE"

    while IFS=$'\t' read -r __ FILE; do
        cat "$FILE"
        rm -f "$FILE"
    done < <(exec sort -n "$TEMPLISTFILE") > "$OUTPUT"

    rm -f "$TEMPLISTFILE"
done
于 2013-09-20T13:17:58.137 回答