python2.7 + wxpython +zbar 实现二维码的生成、嵌入和提取
2018年7月25日23:40:43 【原创】
目录:python 编程博客 索引
1. 运行环境
最近打算用 python 写一个工具打算实现二维码的生成、嵌入和读取功能。
但是搭建环境问题确实难住了我
通过查阅各种资料,发现 zbar 模块不支持 python3,只好使用 python2 来实现
但是还有一个问题就是 python2.6 和 python 2.7 是不同的
网上有一种说法是 zbar 最高支持 python2.6,但是我仍然在国外的网站发现了 zbar-0.10.win32-py2.7_2.msi,可以支持 python2.7
通过实践,python2.6 及其所配套的 zbar-0.10.win32-py2.6.exe 在 为 win7 32位的环境中跑不起来
最终还是选择使用 python2.7 和 zbar-0.10.win32-py2.7_2.msi
在这里,实现环境算是很简单了,只需要下载了 python2.7 和 zbar-0.10.win32-py2.7_2.msi 即可
另外我的环境使用了 wxpython 图形化模块,安装方法也很简单
pip install wxpython
pip install pyinstaller
wxpython 已经更新到版本 4.0 了,在这里的小软件中的环境是可以兼容使用的
打包成文件
pyinstaller -F -w -i C:\Users\John\Desktop\pythonworkV4.0\icon.ico C:\Users\John\Desktop\pythonworkV4.0\setup.py
另外,为了方便大家使用 zbar 环境,我把使用到的虚拟机环境上传到百度云网盘中
以上环境所使用到的工具都在这里
链接:https://pan.baidu.com/s/1t4xUub250SjLJ2WRc32HLg 提取码:n922
有问题可以留言或者私聊我QQ 781670201。
有代做毕业设计软件的可以联系,不贵。我写过的部分毕业设计在博客链接中有。
2. 功能简介
-
功能
- 自定义二维码信息内容
- 将信息生成二维码
- 将二维码嵌入到某个图片的右下角
- 在图片上提取二维码信息并输出到主界面
-
主界面
-
代码下载
3. 程序代码
# coding:UTF-8
import wx
import zbar
from PIL import Image
'''
pil处理图片,验证,处理
大小,格式 过滤
压缩,截图,转换
图片库最好用Pillow
还有一个测试图片test.jpg, 一个log图片,一个字体文件
'''
#图片的基本参数获取
try:
from PIL import Image, ImageDraw, ImageFont, ImageEnhance
except ImportError:
import Image, ImageDraw, ImageFont, ImageEnhance
import qrcode
import os
wildcard1 = u" w图像文件 (*.png)|*.bmp|" \
"All files (*.*)|*.*"
global img
fileDir = os.getcwd()
class MultiTextFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, u"基于二维码的数字水印嵌入及提取工具",size=(600, 400))
#wx.Frame.__init__(self,image,parent = None,id = -1, pos = wx.DefaultPosition,title = u"隐藏信息的图片为:",size=(800,600))
panel = wx.Panel(self, -1)
font = wx.Font(12, wx.ROMAN, wx.NORMAL, wx.BOLD, False)
font.SetPointSize(14)
font2 = wx.Font(12, wx.ROMAN, wx.NORMAL, wx.BOLD, False)
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add((-1, 15))
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
# st1
st1 = wx.StaticText(panel, label=u'待隐藏水印')
st1.SetFont(font2)
hbox1.Add(st1, flag=wx.RIGHT, border=10)
self.tc = wx.TextCtrl(panel, size=(400,80),style=wx.TE_MULTILINE | wx.TE_RICH2) # wx.HSCROLL 不自动换行
self.tc.SetFont(font)
hbox1.Add(self.tc, proportion=1, flag=wx.EXPAND)
vbox.Add(hbox1, proportion=1, flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=10)
vbox.Add((-1, 15))
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
# st2
st2 = wx.StaticText(panel, label=u'提取二维码')
st2.SetFont(font2)
hbox3.Add(st2,flag=wx.RIGHT, border=10)
self.tc2 = wx.TextCtrl(panel, size=(400,80),style=wx.TE_MULTILINE | wx.TE_RICH2 | wx.TE_READONLY) # wx.HSCROLL 不自动换行
self.tc2.SetFont(font)
hbox3.Add(self.tc2, proportion=1, flag=wx.EXPAND)
vbox.Add(hbox3, proportion=1, flag=wx.LEFT | wx.RIGHT | wx.EXPAND,border=10)
vbox.Add((-1, 20))
hbox5 = wx.BoxSizer(wx.HORIZONTAL)
# btn1
btn1 = wx.Button(panel, label=u'生成二维码', size=(120, 30))
btn1.SetFont(font)
hbox5.Add(btn1)
# btn2
btn2 = wx.Button(panel, label=u'添加水印', size=(120, 30))
btn2.SetFont(font)
hbox5.Add(btn2, flag=wx.LEFT | wx.BOTTOM, border=50)
# btn3
btn3 = wx.Button(panel, label=u'提取二维码信息', size=(150, 30))
btn3.SetFont(font)
hbox5.Add(btn3, flag=wx.LEFT | wx.BOTTOM, border=50)
vbox.Add(hbox5, flag=wx.ALIGN_CENTER_HORIZONTAL | wx.RIGHT, border=10)
panel.SetSizer(vbox)
self.Bind(wx.EVT_BUTTON, self.creat_logo, btn1)
self.Bind(wx.EVT_BUTTON, self.logo_watermark, btn2)
self.Bind(wx.EVT_BUTTON, self.put_message, btn3)
self.b = 0
def creat_logo(self, event):
qr = qrcode.QRCode(
version=7,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=3,
border=4
)
#global messages
#self.tc.Clear()
self.tc2.Clear()
tc1data = self.tc.GetValue()
qr.add_data(tc1data)
qr.make(fit=True)
img = qr.make_image()
if tc1data == '':
self.messagedata = '文本框内容不可以为空'
dlg = wx.MessageDialog(self, self.messagedata, caption='提示:', style = wx.OK|wx.ICON_EXCLAMATION)
dlg.ShowModal()
return
else:
with wx.FileDialog(self, "保存文件", defaultDir=os.getcwd(),defaultFile="QR_code.png", wildcard=wildcard1,
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# save the current contents in the file
global pathname
pathname = fileDialog.GetPath()
img.save(pathname)
image = wx.Image(pathname,wx.BITMAP_TYPE_PNG)
self.frame3 = Frame2(image)
self.frame3.Show()
#self.SetTopWindow(self.frame)
return True
def logo_watermark(self, event):
'''''
添加一个图片水印,原理就是合并图层,用png比较好
'''
with wx.FileDialog(self, "打开二维码文件", defaultDir=os.getcwd(), defaultFile="QR_code.png", wildcard=wildcard1,
style=wx.FD_OPEN) as fileDialog_logo:
if fileDialog_logo.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
logo_path = fileDialog_logo.GetPath()
with wx.FileDialog(self, "打开载体文件", defaultDir=os.getcwd(), defaultFile="gaomin.png", wildcard=wildcard1,
style=wx.FD_OPEN) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# save the current contents in the file
pathname2 = fileDialog.GetPath()
im = Image.open(pathname2) # 打开载体
baseim = im
baseim = Image.open(pathname2) # 打开载体
logoim = Image.open(logo_path)
bw, bh = baseim.size
lw, lh = logoim.size
baseim.paste(logoim, (bw-lw, bh-lh))
with wx.FileDialog(self, "保存隐藏信息的文件", defaultDir=os.getcwd(), defaultFile="secret.png", wildcard=wildcard1,
style=wx.FD_SAVE) as fileDialog2:
if fileDialog2.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# save the current contents in the file
pathname3 = fileDialog2.GetPath()
baseim.save(pathname3, 'png')
#print(u'logo水印组合成功')
image3 = wx.Image(pathname3,wx.BITMAP_TYPE_PNG)
#wx.StaticBitmap(self,-1,wx.BitmapFromImage(image3))
w = image3.GetWidth()
h = image3.GetHeight()
image3.Rescale(w/2,h/2) #修改显示大小
self.frame3 = Frame3(image3)
self.frame3.Show()
#self.SetTopWindow(self.frame)
return True
#水印提取
def put_message(self, event):
'''
先按照一个比例对图片剪裁,然后再压缩到指定尺寸
一个图片 16:5 ,压缩为 2:1 并且宽为200,就要先把图片裁剪成 10:5,然后在等比压缩
'''
qua=95
# (im, dst_w, dst_h, qua=95):
with wx.FileDialog(self, "打开待提取信息的图像文件", defaultDir=os.getcwd(), defaultFile="secret.png", wildcard=wildcard1,
style=wx.FD_OPEN) as fileDialog:
if fileDialog.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# save the current contents in the file
pathname4 = fileDialog.GetPath()
im = Image.open(pathname4) #image 对象
ori_w,ori_h = im.size
x,y = 159, 159
#裁剪
box = (ori_w-x,ori_h-y,ori_w,ori_h)
#所包围的图像,crop方法与php中的imagecopy方法大为不一样
newIm = im.crop(box)
print(u"提取完成")
'''
with wx.FileDialog(self, "保存提取的二维码的文件", defaultFile="QR_code.png", wildcard=wildcard1,
style=wx.FD_SAVE) as fileDialog2:
if fileDialog2.ShowModal() == wx.ID_CANCEL:
return # the user changed their mind
# save the current contents in the file
pathname5 = fileDialog2.GetPath()
newIm.save(pathname5, 'PNG')
print("old size %s %s"%(ori_w, ori_h))
print(u"提取完成")
'''
# 二维码识别
scanner = zbar.ImageScanner()
scanner.parse_config('enable')
#img = Image.open(pathname5).convert('L')
img = newIm.convert('L')
w, h = img.size
zimg = zbar.Image(w, h, 'Y800', img.tobytes())
# 判断是二维码
raw = img.tobytes()
a_bytes = raw[1:10].encode('hex')
# print a_bytes
if a_bytes == 'ffffffffffffffffff':
dlg = wx.MessageDialog(self, u"二维码水印信息提取成功", u"提示", wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
scanner.scan(zimg)
for s in zimg:
# print s.type,s.data
print u'提取的二维码水印信息为:%s' % s.data
self.tc2.Clear()
self.tc2.AppendText(s.data)
'''
image = wx.Image(pathname5,wx.BITMAP_TYPE_PNG)
self.frame3 = Frame2(image)
self.frame3.Show()
'''
else:
print u"图像中未保存二维码水印信息"
dlg = wx.MessageDialog(self, u"图像中未保存二维码水印信息", u"提示", wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
class Frame2(wx.Frame):
def __init__(self,image,parent = None,id = -1, pos = wx.DefaultPosition,title = u"二维码图片为:",size=(400,400)):
#显示图片
tmp = image.ConvertToBitmap()
size = tmp.GetWidth(),tmp.GetHeight()
wx.Frame.__init__(self,parent,id,title,pos,(400,400))
self.bmp = wx.StaticBitmap(parent=self, bitmap=tmp)
class Frame3(wx.Frame):
def __init__(self,image,parent = None,id = -1, pos = wx.DefaultPosition,title = u"隐藏信息的图片为:",size=(800,600)):
#显示图片
tmp = image.ConvertToBitmap()
size = tmp.GetWidth(),tmp.GetHeight()
wx.Frame.__init__(self,parent,id,title,pos,(800,600))
self.bmp = wx.StaticBitmap(parent=self, bitmap=tmp)
class MyApp(wx.App):
def __init__(self):
# 重构__init__方法,将错误信息重定位到文件中;
# 默认redirect=True,输出到StdOut或StdError;
# 为防止程序因错误一闪而过无法捕捉信息,可在
# 控制台中使用python -i example.py来运行程序。
wx.App.__init__(self, redirect=False, filename=r"./IO.txt")
def OnInit(self):
frame = MultiTextFrame()
frame.Show(True)
return True
def main():
app = MyApp()
app.MainLoop()
if __name__ == "__main__":
main()