Open dovy opened 2 years ago
This would be very cool. Found this issue after using QRcode-monkey as well. I really like some of the rounded square options.
Just figured out that you can use the custom module drawers on the eyes like this:
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
from qrcode.image.styles.colormasks import RadialGradiantColorMask
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_L)
qr.add_data("Some data")
img_1 = qr.make_image(
image_factory=StyledPilImage,
module_drawer=RoundedModuleDrawer(),
eye_drawer=RoundedModuleDrawer(),
)
Haven't yet figured out how to use the different eye shapes though, like a dot, or a partially rounded square, for example.
I was just looking into using the QR Code Monkey API too for more customisations, but it is paid.
Is there any way to change the colour of the eyes?
Just figured out that you can use the custom module drawers on the eyes like this:
import qrcode from qrcode.image.styledpil import StyledPilImage from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer from qrcode.image.styles.colormasks import RadialGradiantColorMask qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_L) qr.add_data("Some data") img_1 = qr.make_image( image_factory=StyledPilImage, module_drawer=RoundedModuleDrawer(), eye_drawer=RoundedModuleDrawer(), )
Haven't yet figured out how to use the different eye shapes though, like a dot, or a partially rounded square, for example.
I did it this way! I want to change color QR code?
I want to change color QR code?
Add the color_mask
parameter to qr.make_image()
img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=RoundedModuleDrawer(radius_ratio=1),
eye_drawer=RoundedModuleDrawer(radius_ratio=1),
color_mask=SolidFillColorMask(back_color=(255, 255, 255), front_color=(0, 0, 0)),
embeded_image_path="path/to/image",
)
I wrote a custom function to do this for me based on the standard positions of the Finder Patterns on QR codes:
from PIL import Image, ImageDraw
def style_eyes(img):
img_size = img.size[0]
eye_size = 70 #default
quiet_zone = 40 #default
mask = Image.new('L', img.size, 0)
draw = ImageDraw.Draw(mask)
draw.rectangle((40, 40, 110, 110), fill=255)
draw.rectangle((img_size-110, 40, img_size-40, 110), fill=255)
draw.rectangle((40, img_size-110, 110, img_size-40), fill=255)
return mask
Then masked the two images together:
mask = style_eyes(qr_img)
final_img = Image.composite(qr_eyes_img, qr_img, mask)
final_img
I made the two images with the standard code form the README doc:
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data('http://www.amazon.com')
qr_eyes_img = qr.make_image(image_factory=StyledPilImage,
eye_drawer=RoundedModuleDrawer(radius_ratio=1.2),
color_mask=SolidFillColorMask(back_color=(255, 255, 255), front_color=(255, 110, 0)))
qr_img = qr.make_image(image_factory=StyledPilImage,
module_drawer=CircleModuleDrawer(),
color_mask=SolidFillColorMask(front_color=(59, 89, 152)),
embeded_image_path="/content/Facebook-logo (1).png")
qr_eyes_img
qr_img
mask
final_img
PS this works for all size QR codes.
I was trying to change the eye/location marker colors as well to be different colors. @Louw-Reegan 's solution takes ~1 second to run which seemed a bit slow. I found the real issue is the color_mask's that slow things down.
I recreated the moduledrawers into my own library that can be imported and used instead of the default moduledrawers. https://github.com/xtream1101/qrcode-xcolor
This can replicate the above output in only ~0.043 seconds vs the ~1 second the other solution takes.
I hope others find this helpful.
Updated the code for custom box and border size
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer
from qrcode.image.styles.colormasks import SolidFillColorMask
from PIL import Image, ImageDraw
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H , box_size=20 ,border= 2)
qr.add_data('data')
qr_eyes_img = qr.make_image(
image_factory=StyledPilImage,
eye_drawer=RoundedModuleDrawer(radius_ratio=1.2),
color_mask=SolidFillColorMask(back_color=(255, 255, 255), front_color=(199 , 8 ,8 ))
)
qr_img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=RoundedModuleDrawer(),
embeded_image_path='logo.png'
)
def style_eyes(img):
img_size = img.size[0]
box_size = img.box_size
border_size = (img_size/box_size - 33 ) /2
quiet_zone = box_size * border_size
eye_size = 7 * box_size
quiet_eye_size = eye_size + quiet_zone
mask = Image.new('L', img.size, 0)
draw = ImageDraw.Draw(mask)
draw.rectangle((quiet_zone, quiet_zone,quiet_eye_size,quiet_eye_size), fill=255)
draw.rectangle((img_size-quiet_eye_size, quiet_zone, img_size-quiet_zone, quiet_eye_size), fill=255)
draw.rectangle((quiet_zone, img_size-quiet_eye_size, quiet_eye_size, img_size-quiet_zone), fill=255)
return mask
mask = style_eyes(qr_eyes_img)
img_rounded = Image.composite(qr_eyes_img, qr_img, mask)
img_rounded.save('qr_rounded.png')
I tried to create a new custom class to use it in eye_drawer
parameter in qr.make_image()
method,
here is a class that will insert a custom image for each cell instead of drawing it :
from qrcode.image.styles.moduledrawers.base import QRModuleDrawer
ANTIALIASING_FACTOR = 4
class StyledPilQRModuleDrawer(QRModuleDrawer):
"""
A base class for StyledPilImage module drawers.
NOTE: the color that this draws in should be whatever is equivalent to
black in the color space, and the specified QRColorMask will handle adding
colors as necessary to the image
"""
img: "StyledPilImage"
class StarModuleDrawer(StyledPilQRModuleDrawer):
"""
Draws the modules as circles
"""
circle = None
def __init__(self, keyTest='None'):
self.keyTest = keyTest
def initialize(self, *args, **kwargs):
super().initialize(*args, **kwargs)
box_size = self.img.box_size
fake_size = box_size * ANTIALIASING_FACTOR
self.circle = Image.new(
self.img.mode,
(fake_size, fake_size),
self.img.color_mask.back_color,
)
ImageDraw.Draw(self.circle).ellipse(
(0, 0, fake_size, fake_size), fill=self.img.paint_color
)
self.circle = self.circle.resize((box_size, box_size), Image.Resampling.LANCZOS)
def drawrect(self, box, is_active: bool):
if is_active:
box_size = self.img.box_size
custom_image = Image.open(f"{base_path}/pattern4.png")
custom_image.resize((box_size, box_size), Image.Resampling.LANCZOS)
self.img._img.paste(custom_image, (box[0][0], box[0][1]),custom_image)
you can see the image path is static here
custom_image = Image.open(f"{base_path}/pattern4.png")
you can just pass it from the constructor in order to make it dynamic!
here assign the custom class StarModuleDrawer
in the make_image()
method
import qrcode
from qrcode.image.styledpil import StyledPilImage
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_H)
qr.add_data('http://www.medium.com')
qr_inner_eyes_img = qr.make_image(image_factory=StyledPilImage,
eye_drawer=StarModuleDrawer(),
color_mask=SolidFillColorMask(back_color=(255, 255, 255), front_color=(63, 42, 86)))
qr_inner_eyes_img.save(f"{base_path}/final_image.png")
The pattern4.png image is 10 x 10 :
Can anyone help me how we can create QR code like below..
Hi,
We need to create the below QR code using python or C#. The body shape will be in diamond shape and eye frame and eye ball will be default square shape. Also logo in center. Could anyone help me?
Hi @subhransujena,
I can assist you with that if you still need help.
@TechnoRahmon Hi..I am still looking for help.
@subhransujena can send me here techno.r@outlook.com
For Anyone who wants to customize their QRCode's eyes, use following code
import abc
from typing import TYPE_CHECKING, Any, Union
import qrcode
from PIL import ImageDraw
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.colormasks import SolidFillColorMask
from qrcode.image.styles.moduledrawers.pil import HorizontalBarsDrawer
from qrcode.main import QRCode
if TYPE_CHECKING:
from qrcode.image.base import BaseImage
from qrcode.main import ActiveWithNeighbors, QRCode
class BaseEyeDrawer(abc.ABC):
needs_processing = True
needs_neighbors = False
factory: "StyledPilImage2"
def initialize(self, img: "BaseImage") -> None:
self.img = img
def draw(self):
(nw_eye_top, _), (_, nw_eye_bottom) = (
self.factory.pixel_box(0, 0),
self.factory.pixel_box(6, 6),
)
(nw_eyeball_top, _), (_, nw_eyeball_bottom) = (
self.factory.pixel_box(2, 2),
self.factory.pixel_box(4, 4),
)
self.draw_nw_eye((nw_eye_top, nw_eye_bottom))
self.draw_nw_eyeball((nw_eyeball_top, nw_eyeball_bottom))
(ne_eye_top, _), (_, ne_eye_bottom) = (
self.factory.pixel_box(0, self.factory.width - 7),
self.factory.pixel_box(6, self.factory.width - 1),
)
(ne_eyeball_top, _), (_, ne_eyeball_bottom) = (
self.factory.pixel_box(2, self.factory.width - 5),
self.factory.pixel_box(4, self.factory.width - 3),
)
self.draw_ne_eye((ne_eye_top, ne_eye_bottom))
self.draw_ne_eyeball((ne_eyeball_top, ne_eyeball_bottom))
(sw_eye_top, _), (_, sw_eye_bottom) = (
self.factory.pixel_box(self.factory.width - 7, 0),
self.factory.pixel_box(self.factory.width - 1, 6),
)
(sw_eyeball_top, _), (_, sw_eyeball_bottom) = (
self.factory.pixel_box(self.factory.width - 5, 2),
self.factory.pixel_box(self.factory.width - 3, 4),
)
self.draw_sw_eye((sw_eye_top, sw_eye_bottom))
self.draw_sw_eyeball((sw_eyeball_top, sw_eyeball_bottom))
@abc.abstractmethod
def draw_nw_eye(self, position): ...
@abc.abstractmethod
def draw_nw_eyeball(self, position): ...
@abc.abstractmethod
def draw_ne_eye(self, position): ...
@abc.abstractmethod
def draw_ne_eyeball(self, position): ...
@abc.abstractmethod
def draw_sw_eye(self, position): ...
@abc.abstractmethod
def draw_sw_eyeball(self, position): ...
class CustomEyeDrawer(BaseEyeDrawer):
def draw_nw_eye(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=None,
width=self.factory.box_size,
outline="black",
radius=self.factory.box_size * 2,
corners=[True, True, False, True],
)
def draw_nw_eyeball(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=True,
outline="black",
radius=self.factory.box_size,
corners=[True, True, False, True],
)
def draw_ne_eye(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=None,
width=self.factory.box_size,
outline="black",
radius=self.factory.box_size * 2,
corners=[True, True, True, False],
)
def draw_ne_eyeball(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=True,
outline="black",
radius=self.factory.box_size,
corners=[True, True, True, False],
)
def draw_sw_eye(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=None,
width=self.factory.box_size,
outline="black",
radius=self.factory.box_size * 2,
corners=[True, False, True, True],
)
def draw_sw_eyeball(self, position):
draw = ImageDraw.Draw(self.img)
draw.rounded_rectangle(
position,
fill=True,
outline="black",
radius=self.factory.box_size,
corners=[True, False, True, True],
)
class StyledPilImage2(StyledPilImage):
def drawrect_context(self, row: int, col: int, qr: QRCode[Any]):
box = self.pixel_box(row, col)
if self.is_eye(row, col):
drawer = self.eye_drawer
if getattr(self.eye_drawer, "needs_processing", False):
return
else:
drawer = self.module_drawer
is_active: Union[bool, ActiveWithNeighbors] = (
qr.active_with_neighbors(row, col)
if drawer.needs_neighbors
else bool(qr.modules[row][col])
)
drawer.drawrect(box, is_active)
def process(self) -> None:
if getattr(self.eye_drawer, "needs_processing", False):
self.eye_drawer.factory = self
self.eye_drawer.draw()
super().process()
qr = QRCode(error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=40)
qr.add_data("Some data")
img_1 = qr.make_image(
image_factory=StyledPilImage2,
module_drawer=HorizontalBarsDrawer(),
eye_drawer=CustomEyeDrawer(),
color_mask=SolidFillColorMask(front_color=(0, 157, 224)),
)
img_1.save("image.png")
Previous code will generate following QRCode.
Basically, you have to inherit the BaseEyeDrawer and populate its abstract methods in order to write your custom eyes. Note that here, I use the eye term for the outer part of an eye and eyeball for the inner part. Each one of BaseEyeDrawer's abstract methods takes a position argument indicating the position of an eye or eyeball in the image as a rectangle. With this information, it is easy to draw the eyes however you like. If you want to set the thickness of the eye lines identical to that of other parts of QRCode, use self.factory.box_size. I tried to follow source code design, therefore, this code probably does not break other functionalities. However, use at your own risk.
Is there anyway to do a custom eye with this framework? Something like this service offers: https://www.qrcode-monkey.com/qr-code-api-with-logo/
It would be great to have this feature. Set our own SVG and we can go to town. :)