Converting PDF to an image using PyMuPDF - python

I have attempted to use PyMuPDF to covert a PDF document to an image, so that I can use it in openCV. However I have an attribute error come up when I try to save the image and I'm not sure how to get around this?
import fitz
pdf = fitz.open('cornwall.pdf')
page = pdf.load_page(0)
pix = page.get_pixmap()
pix.writeImage("cornwall_output.png")
AttributeError: 'Pixmap' object has no attribute 'writeImage'

use pil_save method instead
https://pymupdf.readthedocs.io/en/latest/pixmap.html#Pixmap.pil_save
import fitz
pdf = fitz.open('cornwall.pdf')
page = pdf.load_page(0)
pix = page.get_pixmap()
pix.pil_save("cornwall_output.png")
# optional arg in this method:
# optimize=True

There is a standard way to save a PyMuPDF Pixmap: pix.save(). There is a handful of possible image formats available in this case: PNG, PSD (Adobe Photoshop), PS (Postscript) and the less popular PAM, PBM, PGM, PNM, PPM. Use pix.pil_save() instead only if you need more alternatives (e.g. JPEG) or special features offered by Pillow.

Related

Time efficient way to convert PDF to image

Context:
I have PDF files I'm working with.
I'm using an ocr to extract the text from these documents and to be able to do that I have to convert my pdf files to images.
I currently use the convert_from_path function of the pdf2image module but it is very time inefficient (9minutes for a 9page pdf).
Problem:
I am looking for a way to accelerate this process or another way to convert my PDF files to images.
Additional info:
I am aware that there is a thread_count parameter in the function but after several tries it doesn't seem to make any difference.
This is the whole function I am using:
def pdftoimg(fic,output_folder):
# Store all the pages of the PDF in a variable
pages = convert_from_path(fic, dpi=500,output_folder=output_folder,thread_count=9, poppler_path=r'C:\Users\Vincent\Documents\PDF\poppler-21.02.0\Library\bin')
image_counter = 0
# Iterate through all the pages stored above
for page in pages:
filename = "page_"+str(image_counter)+".jpg"
page.save(output_folder+filename, 'JPEG')
image_counter = image_counter + 1
for i in os.listdir(output_folder):
if i.endswith('.ppm'):
os.remove(output_folder+i)
Link to the convert_from_path reference.
I found an answer to that problem using another module called fitz which is a python binding to MuPDF.
First of all install PyMuPDF:
The documentation can be found here but for windows users it's rather simple:
pip install PyMuPDF
Then import the fitz module:
import fitz
print(fitz.__doc__)
>>>PyMuPDF 1.18.13: Python bindings for the MuPDF 1.18.0 library.
>>>Version date: 2021-05-05 06:32:22.
>>>Built for Python 3.7 on win32 (64-bit).
Open your file and save every page as images:
The get_pixmap() method accepts different parameters that allows you to control the image (variation,resolution,color...) so I suggest that you red the documentation here.
def convert_pdf_to_image(fic):
#open your file
doc = fitz.open(fic)
#iterate through the pages of the document and create a RGB image of the page
for page in doc:
pix = page.get_pixmap()
pix.save("page-%i.png" % page.number)
Hope this helps anyone else.

Convert mp3 song image from png to jpg

I have a set of many songs, some of which have png images in metadata, and I need to convert these to jpg.
I know how to convert png images to jpg in general, but I am currently accessing metadata using eyed3, which returns ImageFrame objects, and I don't know how to manipulate these. I can, for instance, access the image type with
print(img.mime_type)
which returns
image/png
but I don't know how to progress from here. Very naively I tried loading the image with OpenCV, but it is either not a compatible format or I didn't do it properly. And anyway I wouldn't know how to update the old image with the new one either!
Note: While I am currently working with eyed3, it is perfectly fine if I can solve this any other way.
I was finally able to solve this, although in a not very elegant way.
The first step is to load the image. For some reason I could not make this work with eyed3, but TinyTag does the job:
from PIL import Image
from tinytag import TinyTag
tag = TinyTag.get(mp3_path, image=True)
image_data = tag.get_image()
img_bites = io.BytesIO(image_data)
photo = Image.open(im)
Then I manipulate it. For example we may resize it and save it as jpg. Because we are using Pillow (PIL) for these operations, we actually need to save the image and finally load it back to get the binary data (this detail is probably what should be improved in the process).
photo = photo.resize((500, 500)) # suppose we want 500 x 500 pixels
rgb_photo = photo.convert("RGB")
rgb_photo.save(temp_file_path, format="JPEG")
The last step is thus to load the image and set it as metadata. You have more details about this step in this answer.:
audio_file = eyed3.load(mp3_path) # this has been loaded before
audio_file.tag.images.set(
3, open(temp_file_path, "rb").read(), "image/jpeg"
)
audio_file.tag.save()

How do I add an image from a list in python using docx?

I wrote a code that takes a screenshot that I want to paste into a word document using docx. So far I have to save the image as a png file. The relevant part of my code is:
from docx import Document
import pyautogui
import docx
doc = Document()
images = []
img = pyautogui.screenshot(region = (some region))
images.append(img)
img.save(imagepath.png)
run =doc.add_picture(imagepath.png)
run
I would like to be able to add the image without saving it. Is it possible to do this using docx?
Yes, according to add_picture — Document objects — python-docx 0.8.10 documentation, add_picture can import data from a stream as well.
As per Screenshot Functions — PyAutoGUI 1.0.0 documentation, screenshot() produces a PIL/Pillow image object which can be save()'d with a BytesIO() as destination to produce a compressed image data stream in memory.
So that'll be:
import io
imdata = io.BytesIO()
img.save(imdata, format='png')
imdata.seek(0)
doc.add_picture(imdata)
del imdata # cannot reuse it for other pictures, you need a clean buffer each time
# can use .truncate(0) then .seek(0) instead but this is probably easier

gfx creating bad quality images from pdf

I am using gfx to convert a particular page in a pdf to a .png image, but the image created is of very bad quality. I need to use gfx and can't use any other module. the code used is:
import gfx
pdf_loc=”C:\new.pdf”
pagenumber=12
doc = gfx.open('pdf',pdf_loc)
page = doc.getPage(page_number)
img = gfx.ImageList()
img.setparameter("antialise", "1") # turn on antialising
img.startpage(page.width,page.height)
page.render(img)
img.endpage()
input_loc="C:\newimg.png"
img.save(input_loc)
You can use the swfrender
add this
gfx.setparameter("zoom", "400")
You can learn more on http://wiki.swftools.org/wiki/Python_gfx_tutorial

Resize image in Python without losing EXIF data

I need to resize jpg images with Python without losing the original image's EXIF data (metadata about date taken, camera model etc.). All google searches about python and images point to the PIL library which I'm currently using, but doesn't seem to be able to retain the metadata. The code I have so far (using PIL) is this:
img = Image.open('foo.jpg')
width,height = 800,600
if img.size[0] < img.size[1]:
width,height = height,width
resized_img = img.resize((width, height), Image.ANTIALIAS) # best down-sizing filter
resized_img.save('foo-resized.jpg')
Any ideas? Or other libraries that I could be using?
There is actually a really simple way of copying EXIF data from a picture to another with only PIL. Though it doesn't permit to modify the exif tags.
image = Image.open('test.jpg')
exif = image.info['exif']
# Your picture process here
image = image.rotate(90)
image.save('test_rotated.jpg', 'JPEG', exif=exif)
As you can see, the save function can take the exif argument which permits to copy the raw exif data in the new image when saving. You don't actually need any other lib if that's all you want to do. I can't seem to find any documentation on the save options and I don't even know if that's specific to Pillow or working with PIL too. (If someone has some kind of link, I would enjoy if they posted it in the comments)
import jpeg
jpeg.setExif(jpeg.getExif('foo.jpg'), 'foo-resized.jpg')
http://www.emilas.com/jpeg/
You can use pyexiv2 to copy EXIF data from source image. In the following example image is resized using PIL library, EXIF data copied with pyexiv2 and image size EXIF fields are set with new size.
def resize_image(source_path, dest_path, size):
# resize image
image = Image.open(source_path)
image.thumbnail(size, Image.ANTIALIAS)
image.save(dest_path, "JPEG")
# copy EXIF data
source_image = pyexiv2.Image(source_path)
source_image.readMetadata()
dest_image = pyexiv2.Image(dest_path)
dest_image.readMetadata()
source_image.copyMetadataTo(dest_image)
# set EXIF image size info to resized size
dest_image["Exif.Photo.PixelXDimension"] = image.size[0]
dest_image["Exif.Photo.PixelYDimension"] = image.size[1]
dest_image.writeMetadata()
# resizing local file
resize_image("41965749.jpg", "resized.jpg", (600,400))
Why not using ImageMagick?
It is quite a standard tool (for instance, it is the standard tool used by Gallery 2); I have never used it, however it has a python interface as well (or, you can also simply spawn the command) and most of all, should maintain EXIF information between all transformation.
Here's an updated answer as of 2018. piexif is a pure python library that for me installed easily via pip (pip install piexif) and worked beautifully (thank you, maintainers!). https://pypi.org/project/piexif/
The usage is very simple, a single line will replicate the accepted answer and copy all EXIF tags from the original image to the resized image:
import piexif
piexif.transplant("foo.jpg", "foo-resized.jpg")
I haven't tried yet, but it looks like you could also perform modifcations easily by using the load, dump, and insert functions as described in the linked documentation.
For pyexiv2 v0.3.2, the API documentation refers to the copy method to carry over EXIF data from one image to another. In this case it would be the EXIF data of the original image over to the resized image.
Going off #Maksym Kozlenko, the updated code for copying EXIF data is:
source_image = pyexiv2.ImageMetadata(source_path)
source_image.read()
dest_image = pyexiv2.ImageMetadata(dest_path)
dest_image.read()
source_image.copy(dest_image,exif=True)
dest_image.write()
You can use pyexiv2 to modify the file after saving it.
from PIL import Image
img_path = "/tmp/img.jpg"
img = Image.open(img_path)
exif = img.info['exif']
img.save("output_"+img_path, exif=exif)
Tested in Pillow 2.5.3
It seems #Depado's solution does not work for me, in my scenario the image does not even contain an exif segment.
pyexiv2 is hard to install on my Mac, instead I use the module pexif https://github.com/bennoleslie/pexif/blob/master/pexif.py. To "add exif segment" to an image does not contain exif info, I added the exif info contained in an image which owns a exif segment
from pexif import JpegFile
#get exif segment from an image
jpeg = JpegFile.fromFile(path_with_exif)
jpeg_exif = jpeg.get_exif()
#import the exif segment above to the image file which does not contain exif segment
jpeg = JpegFile.fromFile(path_without_exif)
exif = jpeg.import_exif(jpeg_exif)
jpeg.writeFile(path_without_exif)
Updated version of Maksym Kozlenko
Python3 and py3exiv2 v0.7
# Resize image and update Exif data
from PIL import Image
import pyexiv2
def resize_image(source_path, dest_path, size):
# resize image
image = Image.open(source_path)
# Using thumbnail, then 'size' is MAX width or weight
# so will retain aspect ratio
image.thumbnail(size, Image.ANTIALIAS)
image.save(dest_path, "JPEG")
# copy EXIF data
source_exif = pyexiv2.ImageMetadata(source_path)
source_exif.read()
dest_exif = pyexiv2.ImageMetadata(dest_path)
dest_exif.read()
source_exif.copy(dest_exif,exif=True)
# set EXIF image size info to resized size
dest_exif["Exif.Photo.PixelXDimension"] = image.size[0]
dest_exif["Exif.Photo.PixelYDimension"] = image.size[1]
dest_exif.write()
PIL handles EXIF data, doesn't it? Look in PIL.ExifTags.

Categories

Resources