Python Wand convert PDF to PNG disable transparent (alpha_channel) - python

I'm trying to convert a PDF to PNG - this all works fine, however, the output image is still transparent even when I believe I have disabled it:
with Image(filename='sample.pdf', resolution=300) as img:
img.background_color = Color("white")
img.alpha_channel = False
img.save(filename='image.png')
The above produces the images but are transparent, I also tried the below:
with Image(filename='sample.pdf', resolution=300, background=Color('white')) as img:
img.alpha_channel = False
img.save(filename='image.png')
which produces this error:
Traceback (most recent call last):
File "file_convert.py", line 20, in <module>
with Image(filename='sample.pdf', resolution=300, background=Color('white')) as img:
File "/Users/Frank/.virtualenvs/wand/lib/python2.7/site-packages/wand/image.py", line 1943, in __init__
raise TypeError("blank image parameters can't be used with image "
TypeError: blank image parameters can't be used with image opening parameters

I also had some PDFs to convert to PNG. This worked for me and seems simpler than compositing images, as shown above.:
from wand.image import Image
from wand.color import Color
all_pages = Image(blob=self.pdf) # PDF will have several pages.
single_image = all_pages.sequence[0] # Just work on first page
with Image(single_image) as i:
i.format = 'png'
i.background_color = Color('white') # Set white background.
i.alpha_channel = 'remove' # Remove transparency and replace with bg.
Reference: wand.image

From a previous answer, try creating an empty image with a background color, then composite over.
from wand.image import Image
from wand.color import Color
with Image(filename="sample.pdf", resolution=300) as img:
with Image(width=img.width, height=img.height, background=Color("white")) as bg:
bg.composite(img,0,0)
bg.save(filename="image.png")

Compiling the other answers, here is the function I use to convert a PDF into pages:
import os
from wand.image import Image
from wand.color import Color
def convert_pdf(filename, output_path, resolution=150):
""" Convert a PDF into images.
All the pages will give a single png file with format:
{pdf_filename}-{page_number}.png
The function removes the alpha channel from the image and
replace it with a white background.
"""
all_pages = Image(filename=filename, resolution=resolution)
for i, page in enumerate(all_pages.sequence):
with Image(page) as img:
img.format = 'png'
img.background_color = Color('white')
img.alpha_channel = 'remove'
image_filename = os.path.splitext(os.path.basename(filename))[0]
image_filename = '{}-{}.png'.format(image_filename, i)
image_filename = os.path.join(output_path, image_filename)
img.save(filename=image_filename)

The other answer (compositing with a white image) works, but only on the last page, as does setting the alpha channel directly. The following works on wand 0.4.2:
im = wand_image(filename='/tmp/foo.pdf', resolution=200)
for i, page in enumerate(im.sequence):
with wand_image(page) as page_image:
page_image.alpha_channel = False
page_image.save(filename='/tmp/foo.pdf.images/page-%s.png' % i)
I think this is probably a bug in wand. It seems like setting the alpha channel for a PDF should affect all pages, but it doesn't.

For those who are still having problem with this, I found solution (it works in version 0.4.1 and above, I am not sure about earlier versions).
So you should just use something like this:
from wand.image import Image
from wand.color import Color
with Image(filename='sample.pdf', resolution=300) as img:
img.background_color = Color("white")
img.alpha_channel = 'remove'
img.save(filename='image.png')

Related

Adjusting size of barcode image output

I am trying to adjust the size of a barcode output
import barcode
from barcode.writer import ImageWriter
bar_class = barcode.get_barcode_class('code128')
barcode = '1234567890'
writer=ImageWriter()
writer.set_options({module_width:2, module_height:2})
code128 = bar_class(barcode, writer)
code128.save('filename')
The error I am getting is:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'module_width' is not defined
I dont really understand how to use the documentation found here: https://pythonhosted.org/pyBarcode/writers/index.html
After you generated an image of any size you can resize it using PIL
import barcode
from barcode.writer import ImageWriter
import PIL
from PIL import Image
bar_class = barcode.get_barcode_class('code128')
barcode = '1234567890'
writer=ImageWriter()
code128 = bar_class(barcode, writer)
code128.save('filename') # save the originally generated image
to_be_resized = Image.open('filename.png') # open in a PIL Image object
newSize = (500, 300) # new size will be 500 by 300 pixels, for example
resized = to_be_resized.resize(newSize, resample=PIL.Image.NEAREST) # you can choose other :resample: values to get different quality/speed results
resized.save('filename_resized.png') # save the resized image
more on PIL.Image.resize
I solved this problem by changing this line
code128.save('filename', {"module_width":0.35, "module_height":10, "font_size": 18, "text_distance": 1, "quiet_zone": 3})
without using another liabrary.
See that!
I solved my problem exactly like this, when saving, I did it as follows::
IMPORTANT: Without another library!
ean = barcode.get('ean13', setbarcodehere, writer=ImageWitoutTextWriter())
filename = ean.save(setfilenamehere, {"module_width":0.35, "module_height":10, "font_size": 18, "text_distance": -3, "quiet_zone": 1})
filename
print('Done! :D, {}'.format(filename))
Example:

Add page title, img2pdf

I've recently found this (wonderful) python software to convert multiple images to a single pdf, img2pdf. After create the first pdf I realized that every page hasn't got any title and it's difficult identify what's the original image (because there're 400), does anyone know how can I add a page title?
Thanks in advance.
I tried to find the same solution but ended up writing a Python program to solve it. I dont know if it helps you but here is a solution nonetheless.
In Python I used PIL.Image and ImageDraw to go through all images and put the filename in each if the images. After that I used img2pdf as a python library to generate the pdf.
Must be run it in the same folder of the images.
import os
import img2pdf
from PIL import Image, ImageDraw, ImageFont, ExifTags
# Enter the path to the font you want, 'fc-list' on ubuntu will get a list of fonts you can use.
#image_text_font = ImageFont.truetype('/Library/Fonts/Arial.ttf', 15)
image_text_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 32)
# Tags the images with 'file name' in the upper left corner
def tag_images():
for file in os.listdir('.'):
if file.endswith(".jpg") and str(file+"_tagged.jpg") not in os.listdir('.') and not file.endswith("_tagged.jpg"):
one_image = check_and_adjust_rotation(Image.open(file))
one_image_draw = ImageDraw.Draw(one_image)
# Add textbox to image
size = one_image_draw.textsize(file, font=image_text_font)
offset = image_text_font.getoffset(file)
one_image_draw.rectangle((10, 10, 10 + size[0] + offset[0], 10 + size[1] + offset[1]), fill='white', outline='black')
# Add text to image
one_image_draw.text((10,10), file, font=image_text_font, fill='black')
# Save tagged image
one_image.save(file + "_tagged.jpg")
print(f'Tagged and saved "{file}_tagged.jpg".')
# Generate the PDF
def generate_pdf_from_multiple_images():
with open("output.pdf", "wb") as f:
f.write(img2pdf.convert([image_file for image_file in os.listdir('.') if image_file.endswith("_tagged.jpg")]))
# Use exif information about rotation to apply proper rotation to the image
def check_and_adjust_rotation(image):
try :
for orientation in ExifTags.TAGS.keys() :
if ExifTags.TAGS[orientation]=='Orientation' : break
exif=dict(image._getexif().items())
print(exif[orientation])
if exif[orientation] == 3 :
image=image.rotate(180, expand=True)
elif exif[orientation] == 6 :
image=image.rotate(270, expand=True)
elif exif[orientation] == 8 :
image=image.rotate(90, expand=True)
except:
traceback.print_exc()
return image
def main():
tag_images()
generate_pdf_from_multiple_images()
if __name__ == '__main__':
main()

Change color scheme when extracting an image from PDF in Python

I am trying to read an image from a pdf following this post:
Extract images from PDF without resampling, in python?
So far I managed to get the image file from the pdf, but it contains a CWYK color scheme and the picture is becoming messed up.
My code is the following:
import PyPDF2
import struct
pdf_filename = 'document.pdf'
pdf_file = open(pdf_filename, 'rb')
cond_scan_reader = PyPDF2.PdfFileReader(pdf_file)
page = cond_scan_reader.getPage(4)
xObject = page['/Resources']['/XObject'].getObject()
for obj in xObject:
print(xObject[obj])
if xObject[obj]['/Subtype'] == '/Image':
if xObject[obj]['/Filter'] == '/DCTDecode':
data = xObject[obj]._data
img = open("image" + ".jpg", "wb")
img.write(data)
img.close()
pdf_file.close()
The point is that when I save, the colors are all weird, I believe it's because of the colorScheme. I have the following in the console:
{'/Type': '/XObject', '/Subtype': '/Image', '/Width': 1122, '/Height': 502, '/Interpolate': <PyPDF2.generic.BooleanObject object at 0x1061574a8>, '/ColorSpace': '/DeviceCMYK', '/BitsPerComponent': 8, '/Filter': '/DCTDecode'}
As you can see, the ColorSpace is CMYK, and I believe that's why the colors of the image are weird.
That's the image I have:
This is the original image (it is inside a pdf file):
Can anyone help me?
Thanks in advance.
Israel
A CMYK mode JPG image that contained in PDF must be invert.
But in PIL, invert of CMYK mode image is not supported.
Than I solve it using numpy.
Full source is in below link.
https://github.com/Gaia3D/pdfImageExtractor/blob/master/extrectImage.py
imgData = np.frombuffer(img.tobytes(), dtype='B')
invData = np.full(imgData.shape, 255, dtype='B')
invData -= imgData
img = Image.frombytes(img.mode, img.size, invData.tobytes())
img.save(outFileName + ".jpg")

Image drawn to reportlab pdf bigger than pdf paper size

i'm writing a program which takes all the pictures in a given folder and aggregates them into a pdf. The problem I have is that when the images are drawn, they are bigger in size and are rotated to the left oddly. I've searched everywhere, havent found anything even in the reportlab documentation.
Here's the code:
import os
from PIL import Image
from PyPDF2 import PdfFileWriter, PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from StringIO import StringIO
def main():
images = image_search()
output = PdfFileWriter()
for image in images:
Image_file = Image.open(image) # need to convert the image to the specific size first.
width, height = Image_file.size
im_width = 1 * cm
# Using ReportLab to insert image into PDF
watermark_str = "watermark" + str(images.index(image)) + '.pdf'
imgDoc = canvas.Canvas(watermark_str)
# Draw image on Canvas and save PDF in buffer
# define the aspect ratio first
aspect = height / float(width)
## Drawing the image
imgDoc.drawImage(image, 0,0, width = im_width, height = (im_width * aspect)) ## at (399,760) with size 160x160
imgDoc.showPage()
imgDoc.save()
# Get the watermark file just created
watermark = PdfFileReader(open(watermark_str, "rb"))
#Get our files ready
pdf1File = open('sample.pdf', 'rb')
page = PdfFileReader(pdf1File).getPage(0)
page.mergePage(watermark.getPage(0))
#Save the result
output.addPage(page)
output.write(file("output.pdf","wb"))
#The function which searches the current directory for image files.
def image_search():
found_images = []
for doc in os.listdir(os.curdir):
image_ext = ['.jpg', '.png', '.PNG', '.jpeg', '.JPG']
for ext in image_ext:
if doc.endswith(ext):
found_images.append(doc)
return found_images
main()
I also tried scaling and specifying the aspect ratio using the im_width variable, which gave the same output.
After a little bit of confusion about your goal I figured out that the goal is to make a PDF overview of the images in the current folder. To do so we actual don't need PyPDF2 as Reportlab offers everything we need for this.
See the code below with the comments as guidelines:
def main():
output_file_loc = "overview.pdf"
imgDoc = canvas.Canvas(output_file_loc)
imgDoc.setPageSize(A4) # This is actually the default page size
document_width, document_height = A4
images = image_search()
for image in images:
# Open the image file to get image dimensions
Image_file = Image.open(image)
image_width, image_height = Image_file.size
image_aspect = image_height / float(image_width)
# Determine the dimensions of the image in the overview
print_width = document_width
print_height = document_width * image_aspect
# Draw the image on the current page
# Note: As reportlab uses bottom left as (0,0) we need to determine the start position by subtracting the
# dimensions of the image from those of the document
imgDoc.drawImage(image, document_width - print_width, document_height - print_height, width=print_width,
height=print_height)
# Inform Reportlab that we want a new page
imgDoc.showPage()
# Save the document
imgDoc.save()

PIL - Convert GIF Frames to JPG

I tried to convert an gif to single images with Python Image Library,
but it results in weird frames
The Input gif is:
Source Image http://longcat.de/gif_example.gif
In my first try, i tried to convert the image with Image.new to an
RGB image, with 255,255,255 as white background - like in any other
example i've found on the internet:
def processImage( infile ):
try:
im = Image.open( infile )
except IOError:
print "Cant load", infile
sys.exit(1)
i = 0
try:
while 1:
background = Image.new("RGB", im.size, (255, 255, 255))
background.paste(im)
background.save('foo'+str(i)+'.jpg', 'JPEG', quality=80)
i += 1
im.seek( im.tell() + 1 )
except EOFError:
pass # end of sequence
but it results in weird output files:
Example #1 http://longcat.de/gif_example1.jpg
My second try was, to convert the gif in an RGBA first, and then use
its transparency mask, to make the transparent pieces white:
def processImage( infile ):
try:
im = Image.open( infile )
except IOError:
print "Cant load", infile
sys.exit(1)
i = 0
try:
while 1:
im2 = im.convert('RGBA')
im2.load()
background = Image.new("RGB", im2.size, (255, 255, 255))
background.paste(im2, mask = im2.split()[3] )
background.save('foo'+str(i)+'.jpg', 'JPEG', quality=80)
i += 1
im.seek( im.tell() + 1 )
except EOFError:
pass # end of sequence
which results in an output like this:
Example #2 http://longcat.de/gif_example2.jpg
The advantage over the first try was, that the first frame looks pretty good
But as you can see, the rest is broken
What should i try next?
Edit:
I think i came a lot closer to the solution
Example #3 http://longcat.de/gif_example3.png
I had to use the palette of the first image for the other images,
and merge it with the previous frame (for gif animations which use
diff-images)
def processImage( infile ):
try:
im = Image.open( infile )
except IOError:
print "Cant load", infile
sys.exit(1)
i = 0
size = im.size
lastframe = im.convert('RGBA')
mypalette = im.getpalette()
try:
while 1:
im2 = im.copy()
im2.putpalette( mypalette )
background = Image.new("RGB", size, (255,255,255))
background.paste( lastframe )
background.paste( im2 )
background.save('foo'+str(i)+'.png', 'PNG', quality=80)
lastframe = background
i += 1
im.seek( im.tell() + 1 )
except EOFError:
pass # end of sequence
But i actually dont know, why my transparency is black, instead of white
Even if i modify the palette (change the transparency channel to white)
or use the transparency mask, the background is still black
First of all, JPEG doesn't support transparency! But that's not the only problem.. As you move to the next frame of the GIF the palette information is lost (problem witn PIL?) - so PIL is unable to correctly convert to the RGBA framework (Hence the first frame is okish, but all the others are screwy). So the work-around is to add the palette back in for every frame, (which is what you were doing in your last code example, but your trouble was that you were saving as RGB not RGBA so you had no alpha/ transparency channel. Also you were doing a few unnecessary things..). Anyhow, here are the .png's with transparency and the corrected code, hope its of some use :)
import Image
import sys
def processImage(infile):
try:
im = Image.open(infile)
except IOError:
print "Cant load", infile
sys.exit(1)
i = 0
mypalette = im.getpalette()
try:
while 1:
im.putpalette(mypalette)
new_im = Image.new("RGBA", im.size)
new_im.paste(im)
new_im.save('foo'+str(i)+'.png')
i += 1
im.seek(im.tell() + 1)
except EOFError:
pass # end of sequence
processImage('gif_example.gif')
When viewing an image on an image viewer, even when transparency is set to zero, it tends to display the image as black. One way to be sure that your image is truly transparent is to merge it over another. The 'emoticon' should be seen whilst not obstructing the other image.Try:
background = Image.open('someimage.jpg') #an existing image
foreground = Image.open('foo.jpg') #one of the above images
background.paste(foreground, (0,0), foreground)
background.save('trial.jpg') #the composite image
Theoretically, if you open 'trial.jpg' in the image viewer and the content of the initial image is preserved and on top of it lies the foo image then you'll know for sure if it's just the image viewer and your images are fine...
source here
Image.open('image.gif').convert('RGB').save('image.jpg')
This works for me. The following example shows converting image.gif to 8 jpg format images with white background.
from PIL import Image
from PIL import GifImagePlugin
def gif2jpg(file_name: str, num_key_frames: int, trans_color: tuple):
"""
convert gif to `num_key_frames` images with jpg format
:param file_name: gif file name
:param num_key_frames: result images number
:param trans_color: set converted transparent color in jpg image
:return:
"""
with Image.open(file_name) as im:
for i in range(num_key_frames):
im.seek(im.n_frames // num_key_frames * i)
image = im.convert("RGBA")
datas = image.getdata()
newData = []
for item in datas:
if item[3] == 0: # if transparent
newData.append(trans_color) # set transparent color in jpg
else:
newData.append(tuple(item[:3]))
image = Image.new("RGB", im.size)
image.getdata()
image.putdata(newData)
image.save('{}.jpg'.format(i))
gif2jpg("image.gif", 8, (255, 255, 255)) # convert image.gif to 8 jpg images with white background

Categories