10

My OS is Ubuntu 12.04. How can I convert a pdf file from gray-scale to black-white? The gray-scale pdf file comes from scanning with gray-scale option, and the black-white scale pdf is required by OCR.


Update:

Following Marco's reply, the B-W pdf isn't good and the original file is here.

  • try scantailor – frostschutz Jul 28 '13 at 12:44
  • scantailor has tons of other useful features when it comes to preparing scans for OCR, and that's the only reason why I suggested it (as a comment, not an answer) – frostschutz Jul 28 '13 at 13:22
  • You can open, and export, (at least some).pdf's in libreoffice (and hence I would guess most modern word processors). Don't know if that will make your desired change possible or easy tho. – goldilocks Jul 28 '13 at 13:26
  • 1
    There's also pdfimages (poppler) to extract scanned images from its PDF container. May be more efficient to handle them with ImageMagick in the first place. – frostschutz Jul 28 '13 at 13:30
9

1) Use ghostscript to convert the PDF to a monochrome PostScript file using the psmono device:

gs -q -sDEVICE=psmono -o mono.ps input.pdf

2) Then convert the monochrome PostScript back to PDF:

ps2pdf mono.ps

EDIT: The psmono device creates a 1-bit half-tone image which is apparently not what you want. I couldn't find a way to specify a threshold using ghostscript, so I resorted to imagemagick. convert internally uses ghostscript to convert the PDF. It then applies the threshold filtering to produce a 1-bit image and uses ghostscript again to create a PDF. Since convert uses a resolution of 75DPI by default, which might not match your actual resolution, you can provide the density argument. And experiment with the threshold setting. The optimal values highly depend on the input file.

convert -density 150 -threshold 50% input.pdf output.pdf
  • Thanks! One problem of running the first command: the original gray-scale pdf is about 25MB, and the running hasn't finished yet after 15min, and the output file mono.ps is already 150MB and still increasing. I am worrying about that. Are there other ways, for example, print to B-W pdf file? – Tim Jul 28 '13 at 13:01
  • @Tim That's not uncommon. PostScript files are uncompressed, the resulting PDF will be smaller. – Marco Jul 28 '13 at 13:05
  • Thanks. It took about 20mins. The B-W pdf isn't good. and the original file is here – Tim Jul 28 '13 at 14:02
  • @Tim horrible quality, not suited for OCR no matter what you do. – frostschutz Jul 28 '13 at 15:53
3

I also had some scanned color pdfs and grayscale pdfs that I wanted to convert to bw. I tried using gs with the code listed here, and image quality is good with pdf text still there. However, that gs code only converts to grayscale (as asked in the question) and still has large file size. convert yields very poor results when used directly.

I wanted bw pdfs with good image quality and small file size. My solution uses gs to extract grayscale bmp files from the pdf, convert to threshold those bmps to bw and save them as tiff files, and then img2pdf to compress the tiff images and merge them all into one pdf.

I tried going directly to tiff from the pdf but the quality is not the same so I save each page to bmp. For a one page pdf file, convert does a great job from bmp to pdf. Example:

gs -sDEVICE=bmpgray -dNOPAUSE -dBATCH -r300x300 \
   -sOutputFile=./pdf_image.bmp ./input.pdf

convert ./pdf_image.bmp -threshold 40% -compress zip ./bw_out.pdf

For multiple pages, gs can merge multiple pdf files into one, but img2pdf yields smaller file size than gs. The tiff files must be uncompressed as input to img2pdf. Keep in mind for large numbers of pages, the intermediate bmp and tiff files tend to be large in size. pdftk or joinpdf would be better if they can merge compressed pdf files from convert.

I imagine there is a more elegant solution. However, my method produces results with very good image quality and much smaller file size. To get text back in the bw pdf, run OCR again.

My shell script uses gs, convert, and img2pdf. Change the parameters (# of pages, scan dpi, threshold %, etc) listed in the beginning as needed, and run chmod +x ./pdf2bw.sh . Here is the full script (pdf2bw.sh):

#!/bin/bash

num_pages=12
dpi_res=300
input_pdf_name=color_or_grayscale.pdf
bw_threshold=40%
output_pdf_name=out_bw.pdf
#-------------------------------------------------------------------------
gs -sDEVICE=bmpgray -dNOPAUSE -dBATCH -q -r$dpi_res \
   -sOutputFile=./%d.bmp ./$input_pdf_name
#-------------------------------------------------------------------------
for file_num in `seq 1 $num_pages`
do
  convert ./$file_num.bmp -threshold $bw_threshold \
          ./$file_num.tif
done
#-------------------------------------------------------------------------
input_files=""

for file_num in `seq 1 $num_pages`
do
  input_files+="./$file_num.tif "
done

img2pdf -o ./$output_pdf_name --dpi $dpi_res $input_files
#-------------------------------------------------------------------------
# clean up bmp and tif files used in conversion

for file_num in `seq 1 $num_pages`
do
  rm ./$file_num.bmp
  rm ./$file_num.tif
done
3

Best way I found out there, without quality loss, removes shadows, noise, text from the next page bleeding through etc:

1) First convert pdf to individual images

pdfimages combined_ocr.pdf page

2) Second remove shadows, noise, text from the next page bleeding through (credits to this blog )

ls ./p*.ppm | xargs -L1 -I {} convert {}  -quality 100 -density 300 -fill white -fuzz 80% +opaque "#000000" {}.jpg

3) This to make a pdf file out of every jpg image without loss of either resolution or quality:

ls -1 ./*jpg | xargs -L1 -I {} img2pdf {} -o {}.pdf

4) This to concatenate the pdfpages into one:

pdftk *.pdf cat output combined.pdf

5) And last I add an OCRed text layer that doesn't change the quality of the scan in the pdfs so they can be searchable:

pypdfocr combined.pdf 
2

Actually, if it comes from a scan, the only reasonable way is to use pdfimages and convert the underlying graphics. I used this script to convert it:

#!/bin/sh
if [ -z "$1" -o -z "$2" ]; then
    echo "Syntax: $0 <input.pdf> <output.pdf>"
    exit 1
fi

pdfimages "$1" scan
for a in scan*.ppm; do 
   convert -white-threshold 85% -monochrome $a `basename $a .ppm`.tiff
done
tiffcp scan*.tiff output.tiff
tiff2pdf output.tiff -o "$2" -p A4 -F
rm scan*.ppm scan*.tiff output.tiff

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.