How to properly create a new image with desired size and resolution (PNG, JPG, JPEG)?

Viewed 1064

I would like to create a PNG using Pillow. The PNG is later used for label printing. So, I try to create an new image using PIL, add some text, a polygon and a QR code. However, I find it rather difficult to get the proper image size (related to the label size which is 7.5 cm x 5.5 cm). I only get there by try and error (it is roughly (275, 204)). When saving, the resolution of the PNG is poor and so the print is.

How do I properly calculate the image size and how can I increase the resolution for saving the image as PNG (or JPG or JPEG).

Here is my code so far:

import qrcode
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw 

# Create qr code instance
qr = qrcode.QRCode(
    version = 1,
    error_correction = qrcode.constants.ERROR_CORRECT_H,
    box_size = 4,
    border = .2,
)

text = "test"
# Create new image

img = Image.new('RGB', (275, 204), color = (220,220,220))

# Add data
qr.add_data(text)
qr.make(fit=True)

# Create an image from the QR Code instance
qrc = qr.make_image(fill_color='black', back_color='white')

draw = ImageDraw.Draw(img)

font1 = ImageFont.truetype("arial.ttf", 25)
font2 = ImageFont.truetype("arial.ttf", 60)

draw.text((65, 150),text,(0,0,0),font=font1)
draw.text((115, 120),text,(0,0,0),font=font2)
draw.polygon(((5, 20), (50, 34), (50, 6)), fill=0)
img.paste(qrc, (80,25))

img.show()

#img.save("image.png", dpi = (300,300))
1 Answers

You'll need to set up relative (label) coordinates and sizes (in cm) for all elements, you want to draw, and then calculate the corresponding image coordinates and sizes (in pixels) w.r.t. the desired resolution. That'll get somehow cumbersome, but I can't think of another way to do this independent of the resolution.

In the following, I left out the whole QR code thing to keep things at least a bit readable.

For comparison, your code as is (without the QR code thing) generates this image on my machine:

Comparison

If I open that image in Photoshop, it says 9.7 cm x 7.2 cm at 72 dpi. If I calculate the resolutions by hand, I'll get the following results:

275 pixels / 7.5 cm * (2.54 cm/inch) = ca. 93 dpi
204 pixels / 5.5 cm * (2.54 cm/inch) = ca. 94 dpi

We'll need the 94 dpi later.

I approximated the relative (label) coordinates and sizes from your original image.

Now, first let's see the whole code:

from PIL import Image, ImageDraw, ImageFont

# Set up parameters
w_cm, h_cm = (7.5, 5.5)             # Real label size in cm
res_x, res_y = (300, 300)           # Desired resolution
res_y_old = 94                      # Old y resolution (204 / 5.5 * 2.54)

# Inch-to-cm factor
f = 2.54

# Determine image size w.r.t. resolution
w = int(w_cm / f * res_x)
h = int(h_cm / f * res_y)

# Create new image with proper size
img = Image.new('RGB', (w, h), color=(220, 220, 220))

# Draw elements
draw = ImageDraw.Draw(img)


def draw_text(x_cm, y_cm, font_size):
    font = ImageFont.truetype('arial.ttf', int(font_size / (res_y_old / res_y)))
    x, y = (int(x_cm / f * res_x), int(y_cm / f * res_y))
    draw.text((x, y), 'test', (0, 0, 0), font=font)


# Draw texts
draw_text(1.875, 4, 25)
draw_text(3.15, 3.25, 60)

# Polygon
coords_cm = [(0.15, 0.5), (1.35, 0.9), (1.35, 0.1)]
coords = [(int(c[0] / f * res_x), int(c[1] / f * res_y)) for c in coords_cm]
draw.polygon(tuple(coords), fill=(0, 0, 0))

# Save image
img.save('image.png', dpi=(res_x, res_y))

And, let's see the output:

Output

If I open that image in Photoshop, it says 7.5 cm x 5.5 cm at 300 dpi, so exactly, what we wanted.

Regarding the font sizes: I guess, you set up those font sizes manually to get a certain appearance. Unfortunately, font sizes given in points are adjusted to have a certain height on monitors, but they won't scale for printing (or "printed" images). That's why, we need to scale the fonts w.r.t. the old and new resolution to keep the desired text appearance from your original image.

Everything else is repetitive converting relative coordinates and sizes. Do not hesitate to ask, if further explanations are needed!

----------------------------------------
System information
----------------------------------------
Platform:    Windows-10-10.0.16299-SP0
Python:      3.8.5
Pillow:      8.1.0
----------------------------------------
Related