import qrcode import numpy as np from PIL import Image import base64 correction_map = { 'L': qrcode.constants.ERROR_CORRECT_L, 'M': qrcode.constants.ERROR_CORRECT_M, 'Q': qrcode.constants.ERROR_CORRECT_Q, 'H': qrcode.constants.ERROR_CORRECT_H, } backgrounds = ['white', 'black', 'gray', 'noise'] bg_map = { 'white': 255, 'black': 0, 'gray': 128, } def create_code(text, module_size, margin, background, error_correction, centered, submodule_prop, split=False): qr = qrcode.QRCode( version=1, error_correction=correction_map[error_correction], box_size=module_size, border=margin if margin > 0 else 1, ) qr.add_data(text) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") # find smallest image size multiple of 256 that can fit qr offset_min = 8 * module_size w, h = img.size mask = None if margin == 0: corner_size = 9 * module_size # make a mask that hides the margin when not in the corners mask = np.ones((h, w)).astype(bool) mask[corner_size:-corner_size, :] = False mask[:, corner_size:-corner_size] = False mask[-corner_size:, -corner_size:] = False mask[module_size:-module_size, module_size:-module_size] = True if submodule_prop != 1: submodule_size = round(module_size * submodule_prop) k = (module_size - submodule_size) // 2 # qr_array = np.array(img).reshape((h // module_size, w // module_size, module_size, module_size)) new_mask = np.zeros((h // module_size, module_size, w // module_size, module_size)).astype(bool) new_mask[:, k:k+submodule_size, :, k:k+submodule_size] = True mask = np.logical_and(mask, new_mask.reshape((h, w))) w = (w + 255 + offset_min) // 256 * 256 h = (h + 255 + offset_min) // 256 * 256 # create new image with background chosen by user if background == 'noise': # noise = np.random.randint(0, 256, (h // module_size, w // module_size), dtype=np.uint8) noise = np.random.normal(128, 64, (h // module_size, w // module_size)).astype(np.uint8) # noise = (noise // 128) * 255 noise = np.round(noise / 128) * 128 # clamp values noise = np.clip(noise, 0, 255) bg = Image.fromarray(noise.astype(np.uint8)).resize((w, h), Image.NEAREST) else: bg = Image.new('L', (w, h), bg_map[background]) # paste qr code in the center of the image if centered: coords = ((w - img.size[0]) // 2, (h - img.size[1]) // 2) else: # paste it aligned on module size, closest to center coords = ((w - img.size[0]) // 2 // module_size * module_size, (h - img.size[1]) // 2 // module_size * module_size) if mask is not None: new_img = Image.new('L', img.size, bg_map['gray'] if background == 'noise' else bg_map[background]) new_img.paste(img, mask=Image.fromarray(mask)) bg.paste(new_img, coords) else: bg.paste(img, coords) if split: # isolate the 3 qr markers, paste them onto a new gray image m_size = module_size * 9 new_bg = Image.new('L', (w, h), bg_map['gray']) # create mask for the 3 markers from a numpy array mask = np.zeros((h, w), dtype=bool) mask[coords[1]:coords[1] + m_size, coords[0]:coords[0] + m_size] = True mask[coords[1]:coords[1] + m_size, coords[0] + img.size[0] - m_size:coords[0] + img.size[0]] = True mask[coords[1] + img.size[1] - m_size:coords[1] + img.size[1], coords[0]:coords[0] + m_size] = True # paste the 3 markers on the new image new_bg.paste(bg, mask=Image.fromarray(mask)) return bg, new_bg else: return bg,