代码拉取完成,页面将自动刷新
from os import remove
from tkinter import Tk, Label, Menu, filedialog, SUNKEN, Frame, Text, Entry, Button, END, messagebox
from PIL import Image, ImageTk, ImageFile, UnidentifiedImageError
from encode import encode_bmp, save_encode_img
from decode import extract
ImageFile.LOAD_TRUNCATED_IMAGES = True
class PlaceholderEntry(Entry):
def __init__(self, master=None, placeholder="Enter text here...", **kwargs):
super().__init__(master, **kwargs)
self.placeholder = placeholder
self.insert("0", self.placeholder)
self.bind("<FocusIn>", self._clear_placeholder)
self.bind("<FocusOut>", self._restore_placeholder)
def _clear_placeholder(self, event):
if self.get() == self.placeholder:
self.delete("0", "end")
def _restore_placeholder(self, event):
if not self.get():
self.insert("0", self.placeholder)
class bmp_transfer:
def __init__(self):
self.original_file_path= "" # 原图的路径/待解码的图的路径
self.embedded_file_path = "" # 嵌入图的路径
self.embedded_info="" # 嵌入的信息
self.embedded_img_header="" # 嵌入图片信息,包括header
self.embedded_img_header_others = "" # 嵌入图的其他信息
self.embedded_img_data="" # 嵌入图片信息,包括data
self.extract_info="" # 提取的图片信息
self.max_embed_length_bits=0 # 可嵌入信息的最大长度(bit)
self.max_embed_length_bytes=0 # 可嵌入信息的最大长度(byte)
# 创建一个Tkinter窗口
self.root = Tk()
self.root.geometry("1300x800") # 设置窗口大小
# 按钮组件
self.button_frame = Frame(self.root, width=400, height=300)
self.button_frame.pack_propagate(0)
self.button_frame.grid(row=0, column=0)
self.button_frame.grid_columnconfigure(0, weight=1)
self.button_frame.grid_columnconfigure(1, weight=1)
self.button_frame.grid_rowconfigure(0, weight=1)
self.button_frame.grid_rowconfigure(1, weight=1)
self.read_img_button = Button(self.button_frame, text="读取图片", command=self.open_img)
self.read_img_button.grid(row=0, column=0)
self.embed_button = Button(self.button_frame, text="嵌入", command=self.embed_info)
self.embed_button.grid(row=0, column=1)
self.save_img_button = Button(self.button_frame, text="存储图片", command=self.save_embed_img)
self.save_img_button.grid(row=1, column=0)
self.extract_button = Button(self.button_frame, text="提取", command=self.extract_embed_info)
self.extract_button.grid(row=1, column=1)
# 图片展示组件
self.original_frame=Frame(self.root, width=400, height=300)
self.encoded_frame=Frame(self.root, width=400, height=300)
self.original_frame.pack_propagate(0)
self.encoded_frame.pack_propagate(0)
self.original_label = Label(self.original_frame, text='原图', borderwidth=2, relief=SUNKEN)
self.encoded_label = Label(self.encoded_frame, text='嵌入信息后的图', borderwidth=2, relief=SUNKEN)
self.original_label.pack(fill='both', expand=True)
self.encoded_label.pack(fill='both', expand=True)
self.original_frame.grid(row=1, column=0)
self.encoded_frame.grid(row=1, column=2)
#对话框展示组件
self.text_frame = Frame(self.root, width=400, height=300)
self.text_frame.pack_propagate(0)
self.text_frame.grid(row=1, column=1)
self.upper_text = Text(self.text_frame, height=15, font=("Arial", 20))
self.lower_text = PlaceholderEntry(self.text_frame, placeholder="输入要嵌入的信息/显示提取出的信息", font=("Arial", 20))
self.upper_text.pack(fill='both', expand=True)
self.lower_text.pack(fill='both', expand=True)
self.upper_text.place(x=0, y=0, width=400, height=150)
self.lower_text.place(x=0, y=150, width=400, height=150)
self.upper_text.insert('1.0', '这是要显示计算出来的可嵌入信息的最大长度')
self.upper_text.configure(state='disabled')
# 创建菜单栏
menubar = Menu(self.root)
# 创建菜单选项
file_menu = Menu(menubar, tearoff=0)
file_menu.add_command(label="读取图片", command=self.open_img) # 将open_img函数绑定到菜单项
file_menu.add_command(label="存储图片", command=self.save_embed_img)
file_menu.add_separator()
file_menu.add_command(label="嵌入", command=self.embed_info)
file_menu.add_command(label="提取", command=self.extract_embed_info)
file_menu.add_separator()
file_menu.add_command(label="退出", command=self.root.quit)
menubar.add_cascade(label="菜单", menu=file_menu) # 将菜单选项添加到menubar
self.root.config(menu=menubar) # 添加菜单栏
# 交互函数
def open_img(self):
self.original_file_path = filedialog.askopenfilename() # 打开文件选择对话框
if self.original_file_path: # 确保文件路径不为空
try:
img = Image.open(self.original_file_path)
except UnidentifiedImageError:
messagebox.showwarning('警告', "选择的文件不是图片")
self.original_file_path=""
return
if img.format =='BMP' and img.mode == 'RGB':
width, height = img.size
self.max_embed_length_bits = width * height * 3 # 总共可以嵌入的bit数,每个像素可以嵌入3bits
self.max_embed_length_bytes = self.max_embed_length_bits // 8 # 总共可以嵌入的字节数
elif img.format =='BMP' and img.mode == 'L':
width, height = img.size
self.max_embed_length_bits = width * height # 总共可以嵌入的bit数,每个像素可以嵌入3bits
self.max_embed_length_bytes = self.max_embed_length_bits // 8 # 总共可以嵌入的字节数
else:
messagebox.showerror('错误', "嵌入图像必须是BMP图片的256灰度图或者24位真彩图,当前图片格式为:{}, {}".format(img.format, img.mode))
# print("嵌入图像必须是BMP图片的256灰度图或者24位真彩图,当前图片格式为:{}, {}".format(img.format, img.mode))
return
if img.size[0]>512 or img.size[1]>512:
messagebox.showerror('错误', "嵌入图像大小不超过512*512,当前图片大小为:{}*{}".format(img.size[0], img.size[1]))
# print("嵌入图像大小不超过512*512,当前图片大小为:{}*{}".format(img.size[0], img.size[1]))
return
if img.size[0]%4 !=0 :
messagebox.showerror('错误', "嵌入图像的宽度必须是4的倍数,当前图片宽度为:{}".format(img.size[0]))
# print("嵌入图像的宽度必须是4的倍数,当前图片宽度为:{}".format(img.size[0]))
return
self.upper_text.configure(state='normal')
self.upper_text.delete('1.0', 'end')
self.upper_text.insert('1.0', '当前图像可嵌入信息最大字节数为:{}'.format(self.max_embed_length_bytes))
self.upper_text.configure(state='disabled')
max_size=(400, 300)
#将大图按比例缩小
if img.size[0]>400 or img.size[1]>300:
img.thumbnail(max_size, Image.LANCZOS)
#将小图按比例放大
else:
max_size = (400, 300)
aspect_ratio = img.size[0] / img.size[1]
new_width = min(max_size[0], max_size[1] * aspect_ratio)
new_height = min(max_size[1], max_size[0] / aspect_ratio)
img = img.resize((int(new_width), int(new_height)), Image.LANCZOS)
img_tk = ImageTk.PhotoImage(img)
self.original_label.config(image=img_tk)
self.original_label.image = img_tk # 保持对图像的引用
def embed_info(self):
if self.original_file_path:
self.embedded_info=self.lower_text.get()
if len(self.embedded_info.encode('utf-8'))>self.max_embed_length_bytes:
messagebox.showerror('错误', '信息过长,无法嵌入')
# print('信息过长,无法嵌入')
else:
# print('嵌入信息中')
#调用encode.py中的嵌入函数将信息嵌入,结果先显示在嵌入信息后的图片中
(self.embedded_img_header, self.embedded_img_header_others, self.embedded_img_data)=encode_bmp(self.original_file_path, self.embedded_info)
self.embedded_file_path=self.original_file_path[:-4]+"_embeded"+self.original_file_path[-4:]
save_encode_img(self.embedded_file_path, self.embedded_img_data, self.embedded_img_header, self.embedded_img_header_others)
# 将这个新的bmp文件读取进来,转换成PhotoImage
img = Image.open(self.embedded_file_path)
max_size = (400, 300)
# 将大图按比例缩小
if img.size[0] > 400 or img.size[1] > 300:
img.thumbnail(max_size, Image.LANCZOS)
# 将小图按比例放大
else:
max_size = (400, 300)
aspect_ratio = img.size[0] / img.size[1]
new_width = min(max_size[0], max_size[1] * aspect_ratio)
new_height = min(max_size[1], max_size[0] / aspect_ratio)
img = img.resize((int(new_width), int(new_height)), Image.LANCZOS)
img_tk = ImageTk.PhotoImage(img)
self.encoded_label.config(image=img_tk)
self.encoded_label.image = img_tk # 保持对图像的引用
#删除临时bmp文件
try:
remove(self.embedded_file_path)
messagebox.showinfo('提示', "嵌入成功")
print("移除成功")
except FileNotFoundError:
messagebox.showwarning('警告', "文件 {} 不存在.".format(self.embedded_file_path))
return
# print("文件 {} 不存在.".format(self.embedded_file_path))
else:
messagebox.showerror('错误', "必须选择图片才能嵌入哦~")
# print("必须选择图片才能嵌入哦~")
def save_embed_img(self):
if self.embedded_file_path:
save_encode_img(self.embedded_file_path, self.embedded_img_data, self.embedded_img_header, self.embedded_img_header_others)
messagebox.showinfo('提示',"保存成功,文件位置为:{}".format(self.embedded_file_path))
else:
messagebox.showerror('错误', "还没嵌入图像呢~")
def extract_embed_info(self):
if self.original_file_path:
self.extract_info=extract(self.original_file_path)
self.lower_text.delete(0, END)
self.lower_text.insert(0, self.extract_info)
messagebox.showinfo('提示', "提取成功~,信息已经显示到上面的框框中了~")
else:
messagebox.showerror('错误', "必须选择图片才能提取哦~")
def run(self):
# 启动窗口
self.root.mainloop()
def main():
app = bmp_transfer()
app.run()
if __name__ == "__main__":
main()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。