0

I am trying to add some text next to and under a QR code.

The problem is that I am struggling on how to edit the image into a QR Code + text. The image below is what I would like to have as a result. The function signature can be changed too.

Target Image

This is the code I have so far

""" 
requirements.txt

qrcode==7.4.2
Pillow==8.1.0
opencv-python==4.7.0.68
"""
import os
from pathlib import Path

import cv2
import qrcode
from PIL import Image, ImageFont, ImageDraw, ImageOps

def create_qr_img() -> str:
    QRcode = qrcode.QRCode(
        error_correction=qrcode.constants.ERROR_CORRECT_H,
        box_size=5,
    )
    url = 'google.com'
    QRcode.add_data(url)
    QRcode.make()
    # adding color to QR code
    QRimg = QRcode.make_image(
        fill_color='Black', back_color="white").convert('RGB')
    # save the QR code generated
    out_fp = f'temp_/QR.png'
    QRimg.save(out_fp)
    return out_fp

def add_str_to_img(img_path: str, 
                               str1: str,
                               str2: str,
                               show:bool=False) -> str:

    black_color_rgb = (0,0,0)
    white_color_rgb = (255,255,255)
    img = Image.open(img_path)
    
    #failed attempt 1)
    # expanding the border works only for writing on top or under the QR code
    # but if the string is too long, it gets cut off
    img = ImageOps.expand(img, border=30, fill=white_color_rgb)

    
    # failed attempt 2)
    # add empty space to the left of the QR code
    # exp_cm = 3
    # exp_px = int(exp_cm * 37.79527559055118)
    # new_shape_pixels = (img.width+exp_px, img.height)
    # img = ImageOps.fit(img, new_shape_pixels, method=Image.ANTIALIAS,
    #              #bleed=0.0, centering=(0.5, 0.5)
    # )
    # end failed attempt 2)
    draw = ImageDraw.Draw(img)
    font_path = os.path.join(cv2.__path__[0],'qt','fonts','DejaVuSans.ttf')
    font = ImageFont.truetype(font_path, size=52)
    # on top of the QR code
    draw.text((62,0),str1,(0,0,0),font=font,
              align='center'
            )
    # bottom
    draw.text((0,470),str2,black_color_rgb,font=font,
              align='center',
              )
    print('QR code TO BE generated!')
    out_fp = f'temp_/QR_print.png'
    Path(out_fp).unlink(missing_ok=True)
    img.save(out_fp)
    if show:
        img.show()
    print('QR code generated!')
    return out_fp

if __name__ == '__main__':
    img_path = create_qr_img()
    add_str_to_img(img_path, 
                   'ExampleAboveQr', 
                   'This is some long string. It could be multi-line. 22222222', 
                   show=True)

I think the solution should be something like with ImageOps.fit but I could not get it work how I wanted (see attempt 2)) in code.

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
DaveR
  • 1,696
  • 18
  • 24
  • What are the constraints please? E.g. text to right should be no taller than QR code and no more than twice as wide? Text underneath should be no wider than QR code plus text to right? Something else? – Mark Setchell Feb 24 '23 at 11:47
  • 2
    OpenCV is really for computer vision, it's not ideal for annotation. You'll likely find this much simpler with `wand` see https://stackoverflow.com/a/63089810/2836621 – Mark Setchell Feb 24 '23 at 11:50
  • 2
    It's pretty simple with **ImageMagick** too https://stackoverflow.com/a/58042039/2836621 – Mark Setchell Feb 24 '23 at 11:53
  • Thanks for the input and your questions. For the contrainsts: The text under the QR should be under the QR code, and its size should be 1/3 of the QR code height. The text on the right, should be the same size (1/3 of QR height) and up to 15 chars by row. – DaveR Feb 24 '23 at 16:31
  • See cv2.getTextSize() and cv2.putText() – fmw42 Feb 24 '23 at 17:20
  • Have a look [here](https://stackoverflow.com/a/16377244/20851944) – Paul-ET Feb 25 '23 at 13:46
  • Does this answer your question? [Add Text on Image using PIL](https://stackoverflow.com/questions/16373425/add-text-on-image-using-pil) – hc_dev Feb 27 '23 at 18:28
  • @hc_dev yes indeed, that is what I used! Thanks a lot – DaveR Feb 28 '23 at 06:50

1 Answers1

0

Thanks to the comment from Paul-ET suggesting: Add Text on Image using PIL

My solution

I created a white image with create_background_image and then pasted the QR and the text later.


def cm_to_pixels(cm: float) -> int:
    return int(cm * 37.79527559055118)


def create_background_image(cm_width: float,
                            cm_height: float,
    ) -> Image.Image:
    w, h = cm_to_pixels(cm=cm_width), cm_to_pixels(cm=cm_height)
    # creating new Image object
    img = Image.new("RGB", (w, h), color='white')
    return img

if __name__ == '__main__':
    img_path = create_qr_img()
    # resize it to make it constant
    qr_img = Image.open(img_path)
    qr_img = qr_img.resize((70, 70))
    # start making the background image
    background_img = create_background_image(cm_width=5, cm_height=2.9)
    # paste qr
    background_img.paste(qr_img, (2, 0), qr_img.convert('RGBA'))
    d = ImageDraw.Draw(background_img)
    widht_and_height_pixels=(5, 68)
    font_path = os.path.join(cv2.__path__[0],'qt','fonts','DejaVuSans.ttf')
    font = ImageFont.truetype(font_path, size=15)
        
    d.text(widht_and_height_pixels,
            "123123345345",fill='black',
            font=font
    )
    background_img.show()
hc_dev
  • 8,389
  • 1
  • 26
  • 38
DaveR
  • 1,696
  • 18
  • 24