diff --git a/.gitignore b/.gitignore index 92f392b..4c6e389 100644 --- a/.gitignore +++ b/.gitignore @@ -92,4 +92,5 @@ ENV/ # Rope project settings .ropeproject -setup.py \ No newline at end of file +setup.py +MANIFEST \ No newline at end of file diff --git a/MyQR/__main__.py b/MyQR/__main__.py deleted file mode 100644 index 4127bad..0000000 --- a/MyQR/__main__.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -from MyQR.mylibs import theqrmodule -from PIL import Image - -# Alignment Pattern Locations -alig_location = [ - (6, 18), (6, 22), (6, 26), (6, 30), (6, 34), (6, 22, 38), (6, 24, 42), (6, 26, 46), (6, 28, 50), (6, 30, 54), (6, 32, 58), (6, 34, 62), (6, 26, 46, 66), (6, 26, 48, 70), (6, 26, 50, 74), (6, 30, 54, 78), (6, 30, 56, 82), (6, 30, 58, 86), (6, 34, 62, 90), (6, 28, 50, 72, 94), (6, 26, 50, 74, 98), (6, 30, 54, 78, 102), (6, 28, 54, 80, 106), (6, 32, 58, 84, 110), (6, 30, 58, 86, 114), (6, 34, 62, 90, 118), (6, 26, 50, 74, 98, 122), (6, 30, 54, 78, 102, 126), (6, 26, 52, 78, 104, 130), (6, 30, 56, 82, 108, 134), (6, 34, 60, 86, 112, 138), (6, 30, 58, 86, 114, 142), (6, 34, 62, 90, 118, 146), (6, 30, 54, 78, 102, 126, 150), (6, 24, 50, 76, 102, 128, 154), (6, 28, 54, 80, 106, 132, 158), (6, 32, 58, 84, 110, 136, 162), (6, 26, 54, 82, 110, 138, 166), (6, 30, 58, 86, 114, 142, 170) - ] - -def combine(ver, qr_name, bg_name, colorized, contrast, brightness, save_place): - from PIL import ImageEnhance, ImageFilter - - qr = Image.open(qr_name) - qr = qr.convert('RGBA') if colorized else qr - - bg0 = Image.open(bg_name).convert('RGBA') - con = contrast if contrast else 1.0 - bg0 = ImageEnhance.Contrast(bg0).enhance(con) - bri = brightness if brightness else 1.0 - bg0 = ImageEnhance.Brightness(bg0).enhance(bri) - - if bg0.size[0] < bg0.size[1]: - bg0 = bg0.resize((qr.size[0]-24, (qr.size[0]-24)*int(bg0.size[1]/bg0.size[0]))) - else: - bg0 = bg0.resize(((qr.size[1]-24)*int(bg0.size[0]/bg0.size[1]), qr.size[1]-24)) - - bg = bg0 if colorized else bg0.convert('1') - - aligs = [] - if ver > 1: - aloc = alig_location[ver-2] - for a in range(len(aloc)): - for b in range(len(aloc)): - if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)): - for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)): - for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)): - aligs.append((i,j)) - - for i in range(qr.size[0]-24): - for j in range(qr.size[1]-24): - if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)): - qr.putpixel((i+12,j+12), bg.getpixel((i,j))) - - qr_name = os.path.join(save_place, os.path.splitext(os.path.basename(bg_name))[0] + '_qrcode.png') - qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name) - return qr_name - -def run(): - import argparse - argparser = argparse.ArgumentParser() - argparser.add_argument('WORDs', help = 'The words to produce you QR-code picture, like a URL or a sentence. Please read the README file for the supported characters.') - argparser.add_argument('-v', '--version', type = int, choices = range(1,41), help = 'The version means the length of a side of the QR-Code picture. From little size to large is 1 to 40.') - argparser.add_argument('-l', '--level', choices = list('LMQH'), help = 'Use this argument to choose an Error-Correction-Level: L(Low), M(Medium) or Q(Quartile), H(High). Otherwise, just use the default one: H') - argparser.add_argument('-p', '--picture', help = 'the picture e.g. example_pic.jpg') - argparser.add_argument('-c', '--colorized', action = 'store_true', help = "Produce a colorized QR-Code with your picture. Just works when there is a correct '-p' or '--picture'.") - argparser.add_argument('-con', '--contrast', type = float, help = 'A floating point value controlling the enhancement of contrast. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0') - argparser.add_argument('-bri', '--brightness', type = float, help = 'A floating point value controlling the enhancement of brightness. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0') - args = argparser.parse_args() - - tempdir = os.path.join(os.path.expanduser('~'), '.myqr') - - try: - # the default version depends on WORDs and level - # init as 0 - ver = args.version if args.version else 0 - # the default level is Q - ecl = args.level if args.level else 'H' - - if not os.path.exists(tempdir): - os.makedirs(tempdir) - - try: - ver, qr_name = theqrmodule.get_qrcode(ver, ecl, args.WORDs, tempdir) - except TypeError: - qr_name = args.picture = None - - if args.picture and args.picture[-4:]=='.gif': - print('it takes a while, please wait for minutes...') - - import imageio - - im = Image.open(args.picture) - im.save(os.path.join(tempdir, '0.png')) - while True: - try: - seq = im.tell() - im.seek(seq + 1) - im.save(os.path.join(tempdir, '%s.png' %(seq+1))) - except EOFError: - break - - imsname = [] - for s in range(seq+1): - bg_name = os.path.join(tempdir, '%s.png' % s) - imsname.append(combine(ver, qr_name, bg_name, args.colorized, args.contrast, args.brightness, tempdir)) - - ims = [imageio.imread(pic) for pic in imsname] - qr_name = os.path.splitext(os.path.basename(args.picture))[0] + '_qrcode.gif' - imageio.mimsave(qr_name, ims) - elif args.picture: - qr_name = combine(ver, qr_name, args.picture, args.colorized, args.contrast, args.brightness, os.getcwd()) - elif qr_name: - qr = Image.open(qr_name) - qr_name = os.path.basename(qr_name) - qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name) - - if qr_name: - print('Succeed! \nCheck out your ' +str(ver) + '-' + str(ecl) + ' QR-code at', os.path.abspath(qr_name)) - except: - raise - finally: - import shutil - if os.path.exists(tempdir): - shutil.rmtree(tempdir) -# -if __name__ == '__main__': - run() \ No newline at end of file diff --git a/MyQR/mylibs/theqrmodule.py b/MyQR/mylibs/theqrmodule.py index c788557..04bf772 100644 --- a/MyQR/mylibs/theqrmodule.py +++ b/MyQR/mylibs/theqrmodule.py @@ -2,31 +2,21 @@ from MyQR.mylibs import data, ECC, structure, matrix, draw -supported_chars = r'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ·,.:;+-*/\~!@#$%^&`[]()?_{}|' - -# ver: Version (from 0 to 40) 0 means default +# ver: Version from 1 to 40 # ecl: Error Correction Level (L,M,Q,H) # get a qrcode picture of 3*3 pixels per module def get_qrcode(ver, ecl, str, save_place): - # ver == 0: default that is depending on str and ecl - if ver not in range(41): - print('WARNING: Version Error! Please choose a version from 0 to 40!') - elif ecl not in 'LMQH': - print('WARNING: Level Error! Please choose one of L,M,Q,H!') - elif any(i not in supported_chars for i in str): - print('WARNING: Input Error! Some characters are not supported.') - else: - # Data Coding - ver, data_codewords = data.encode(ver, ecl, str) + # Data Coding + ver, data_codewords = data.encode(ver, ecl, str) - # Error Correction Coding - ecc = ECC.encode(ver, ecl, data_codewords) + # Error Correction Coding + ecc = ECC.encode(ver, ecl, data_codewords) + + # Structure final bits + final_bits = structure.structure_final_bits(ver, ecl, data_codewords, ecc) + + # Get the QR Matrix + qrmatrix = matrix.get_qrmatrix(ver, ecl, final_bits) - # Structure final bits - final_bits = structure.structure_final_bits(ver, ecl, data_codewords, ecc) - - # Get the QR Matrix - qrmatrix = matrix.get_qrmatrix(ver, ecl, final_bits) - - # Draw the picture and Save it, then return the real ver and the absolute name - return ver, draw.draw_qrcode(save_place, qrmatrix) + # Draw the picture and Save it, then return the real ver and the absolute name + return ver, draw.draw_qrcode(save_place, qrmatrix) diff --git a/MyQR/myqr.py b/MyQR/myqr.py new file mode 100644 index 0000000..658f68a --- /dev/null +++ b/MyQR/myqr.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +from MyQR.mylibs import theqrmodule +from PIL import Image + +# Positional parameters +# words: str +# +# Optional parameters +# version: int, from 1 to 40 +# level: str, just one of ('L','M','Q','H') +# picutre: str, a filename of a image +# colorized: bool +# constrast: float +# brightness: float +# save_name: str, the output filename like 'example.png' +# save_dir: str, the output directory +# +# See [https://github.com/sylnsfar/qrcode] for more details! +def run(words, version=1, level='H', picture=None, colorized=False, contrast=1.0, brightness=1.0, save_name=None, save_dir=os.getcwd()): + + supported_chars = r'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ·,.:;+-*/\~!@#$%^&`[]()?_{}|' + + # check every parameter + if not isinstance(words, str) or any(i not in supported_chars for i in words): + raise ValueError('Wrong words! Make sure the characters are supported!') + if not isinstance(version, int) or version not in range(1, 41): + raise ValueError('Wrong version! Please choose a int-type value from 1 to 40!') + if not isinstance(level, str) or len(level)>1 or level not in 'LMQH': + raise ValueError("Wrong level! Please choose a str-type level from {'L','M','Q','H'}!") + if picture: + if not isinstance(picture, str) or not os.path.isfile(picture) or picture[-4:] not in ('.jpg','.png','.bmp','.gif'): + raise ValueError("Wrong picture! Input a filename that exists and be tailed with one of {'.jpg', '.png', '.bmp', '.gif'}!") + if picture[-4:] == '.gif' and save_name and save_name[-4:] != '.gif': + raise ValueError('Wrong save_name! If the picuter is .gif format, the output filename should be .gif format, too!') + if not isinstance(colorized, bool): + raise ValueError('Wrong colorized! Input a bool-type value!') + if not isinstance(contrast, float): + raise ValueError('Wrong contrast! Input a float-type value!') + if not isinstance(brightness, float): + raise ValueError('Wrong brightness! Input a float-type value!') + if save_name and (not isinstance(save_name, str) or save_name[-4:] not in ('.jpg','.png','.bmp','.gif')): + raise ValueError("Wrong save_name! Input a filename tailed with one of {'.jpg', '.png', '.bmp', '.gif'}!") + if not os.path.isdir(save_dir): + raise ValueError('Wrong save_dir! Input a existing-directory!') + + + def combine(ver, qr_name, bg_name, colorized, contrast, brightness, save_dir, save_name=None): + + # Alignment Pattern Locations + alig_location = [ + (6, 18), (6, 22), (6, 26), (6, 30), (6, 34), (6, 22, 38), (6, 24, 42), (6, 26, 46), (6, 28, 50), (6, 30, 54), (6, 32, 58), (6, 34, 62), (6, 26, 46, 66), (6, 26, 48, 70), (6, 26, 50, 74), (6, 30, 54, 78), (6, 30, 56, 82), (6, 30, 58, 86), (6, 34, 62, 90), (6, 28, 50, 72, 94), (6, 26, 50, 74, 98), (6, 30, 54, 78, 102), (6, 28, 54, 80, 106), (6, 32, 58, 84, 110), (6, 30, 58, 86, 114), (6, 34, 62, 90, 118), (6, 26, 50, 74, 98, 122), (6, 30, 54, 78, 102, 126), (6, 26, 52, 78, 104, 130), (6, 30, 56, 82, 108, 134), (6, 34, 60, 86, 112, 138), (6, 30, 58, 86, 114, 142), (6, 34, 62, 90, 118, 146), (6, 30, 54, 78, 102, 126, 150), (6, 24, 50, 76, 102, 128, 154), (6, 28, 54, 80, 106, 132, 158), (6, 32, 58, 84, 110, 136, 162), (6, 26, 54, 82, 110, 138, 166), (6, 30, 58, 86, 114, 142, 170) + ] + + from PIL import ImageEnhance, ImageFilter + + qr = Image.open(qr_name) + qr = qr.convert('RGBA') if colorized else qr + + bg0 = Image.open(bg_name).convert('RGBA') + bg0 = ImageEnhance.Contrast(bg0).enhance(contrast) + bg0 = ImageEnhance.Brightness(bg0).enhance(brightness) + + if bg0.size[0] < bg0.size[1]: + bg0 = bg0.resize((qr.size[0]-24, (qr.size[0]-24)*int(bg0.size[1]/bg0.size[0]))) + else: + bg0 = bg0.resize(((qr.size[1]-24)*int(bg0.size[0]/bg0.size[1]), qr.size[1]-24)) + + bg = bg0 if colorized else bg0.convert('1') + + aligs = [] + if ver > 1: + aloc = alig_location[ver-2] + for a in range(len(aloc)): + for b in range(len(aloc)): + if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)): + for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)): + for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)): + aligs.append((i,j)) + + for i in range(qr.size[0]-24): + for j in range(qr.size[1]-24): + if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)): + qr.putpixel((i+12,j+12), bg.getpixel((i,j))) + + qr_name = os.path.join(save_dir, os.path.splitext(os.path.basename(bg_name))[0] + '_qrcode.png') if not save_name else os.path.join(save_dir, save_name) + qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name) + return qr_name + + + + + + tempdir = os.path.join(os.path.expanduser('~'), '.myqr') + + try: + if not os.path.exists(tempdir): + os.makedirs(tempdir) + + ver, qr_name = theqrmodule.get_qrcode(version, level, words, tempdir) + + if picture and picture[-4:]=='.gif': + import imageio + + im = Image.open(picture) + im.save(os.path.join(tempdir, '0.png')) + while True: + try: + seq = im.tell() + im.seek(seq + 1) + im.save(os.path.join(tempdir, '%s.png' %(seq+1))) + except EOFError: + break + + imsname = [] + for s in range(seq+1): + bg_name = os.path.join(tempdir, '%s.png' % s) + imsname.append(combine(ver, qr_name, bg_name, colorized, contrast, brightness, tempdir)) + + ims = [imageio.imread(pic) for pic in imsname] + qr_name = os.path.join(save_dir, os.path.splitext(os.path.basename(picture))[0] + '_qrcode.gif') if not save_name else os.path.join(save_dir, save_name) + imageio.mimsave(qr_name, ims) + elif picture: + qr_name = combine(ver, qr_name, picture, colorized, contrast, brightness, save_dir, save_name) + elif qr_name: + qr = Image.open(qr_name) + qr_name = os.path.join(save_dir, os.path.basename(qr_name)) if not save_name else os.path.join(save_dir, save_name) + qr.resize((qr.size[0]*3, qr.size[1]*3)).save(qr_name) + + return ver, level, qr_name + + + except: + raise + finally: + import shutil + if os.path.exists(tempdir): + shutil.rmtree(tempdir) \ No newline at end of file diff --git a/MyQR/terminal.py b/MyQR/terminal.py new file mode 100644 index 0000000..577bc81 --- /dev/null +++ b/MyQR/terminal.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from MyQR.myqr import run +import os + +def main(): + import argparse + argparser = argparse.ArgumentParser() + argparser.add_argument('Words', help = 'The words to produce you QR-code picture, like a URL or a sentence. Please read the README file for the supported characters.') + argparser.add_argument('-v', '--version', type = int, choices = range(1,41), default = 1, help = 'The version means the length of a side of the QR-Code picture. From little size to large is 1 to 40.') + argparser.add_argument('-l', '--level', choices = list('LMQH'), default = 'H', help = 'Use this argument to choose an Error-Correction-Level: L(Low), M(Medium) or Q(Quartile), H(High). Otherwise, just use the default one: H') + argparser.add_argument('-p', '--picture', help = 'the picture e.g. example.jpg') + argparser.add_argument('-c', '--colorized', action = 'store_true', help = "Produce a colorized QR-Code with your picture. Just works when there is a correct '-p' or '--picture'.") + argparser.add_argument('-con', '--contrast', type = float, default = 1.0, help = 'A floating point value controlling the enhancement of contrast. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0') + argparser.add_argument('-bri', '--brightness', type = float, default = 1.0, help = 'A floating point value controlling the enhancement of brightness. Factor 1.0 always returns a copy of the original image, lower factors mean less color (brightness, contrast, etc), and higher values more. There are no restrictions on this value. Default: 1.0') + argparser.add_argument('-n', '--name', help = "The filename of output tailed with one of {'.jpg', '.png', '.bmp', '.gif'}. eg. exampl.png") + argparser.add_argument('-d', '--directory', default = os.getcwd(), help = 'The directory of output.') + args = argparser.parse_args() + + if args.picture and args.picture[-4:]=='.gif': + print('It may take a while, please wait for minutes...') + + try: + ver, ecl, qr_name = run( + args.Words, + args.version, + args.level, + args.picture, + args.colorized, + args.contrast, + args.brightness, + args.name, + args.directory + ) + print('Succeed! \nCheck out your', str(ver) + '-' + str(ecl), 'QR-code:', qr_name) + except: + raise \ No newline at end of file diff --git a/myqr.py b/myqr.py index e1c15bd..ae644bb 100644 --- a/myqr.py +++ b/myqr.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# To be used just when PyInstaller is not installed +# To be used just when MyQR is not installed -from MyQR.__main__ import run +from MyQR.terminal import main -run() \ No newline at end of file +main() \ No newline at end of file