批量图片信息提取工具

查看 75|回复 10
作者:pythonfun   
一、软件名称:批量图片信息提取工具
二、软件功能:
无人职守就可以完成提取图片信息的任务,但是就得消耗豆包API的额度,不过貌似价格不是很高。在编程前,可以去申请一个豆包api,申请地址是:www.volcengine.com
一款图片信息提取工具,实现包括图片的导入、预览、信息提取、复制等功能,基本的样式如下:


微信图片_20250808172657.png (193.38 KB, 下载次数: 0)
下载附件
2025-8-8 17:27 上传

三、代码展示:
[Python] 纯文本查看 复制代码import os
import json
from tkinter import Tk, Label, Entry, filedialog, messagebox, StringVar, END, Button, LEFT,LabelFrame,Frame, Toplevel
from tkinter.scrolledtext import ScrolledText
from tkinter import PhotoImage, font
import threading
import base64
import pyperclip
from pathlib import Path
from openai import OpenAI
from PIL import Image, ImageTk
class ImageEssayEvaluator:
    def __init__(self, root):
        self.root = root
        self.root.title("图片信息提取工具 | By Gordon")
        self.setup_ui()
        self.api = None
        self.output_dir = os.path.join(os.getcwd(), "识别结果")
        os.makedirs(self.output_dir, exist_ok=True)
        self.current_scale = 1.0  # Track current zoom scale
        self.image_files = []  # List to store multiple image paths
        self.current_image_index = 0  # Track current image index
        json_filename = "config.json"
        if not os.path.exists(json_filename):
            with open(json_filename, "w") as json_file:
                json.dump({"api_key": self.api}, json_file)
            print("JSON 文件已创建。")
        else:
            with open(json_filename, "r") as json_file:
                data = json.load(json_file)
            self.api = data.get("api")
        if self.api is None:
            self.show_settings()
        self.update_api_button_text()
    def update_api_button_text(self):
        """根据API状态更新按钮文本"""
        if self.api:
            self.api_button.config(text="已加载API")
        else:
            self.api_button.config(text="设置API")
    def setup_ui(self):
        self.info_var = StringVar()
        self.info_var.set("Tip: 请导入图片,识别结果将显示在下方文本框")
        Label(self.root, textvariable=self.info_var, font=("微软雅黑", 12)).pack(pady=5)
        frame = Frame(self.root)
        frame.pack(padx=6)
        Label(frame, text="图片路径:", font=("微软雅黑", 12)).pack(side='left')
        self.image_path_entry = Entry(frame, width=60, font=("微软雅黑", 12))
        self.image_path_entry.pack(side=LEFT, padx=5, pady=5)
        self.api_button = Button(frame, text="设置API", font=("微软雅黑", 12), command=self.show_settings)
        self.api_button.pack()
        # Create a frame for the image and text display
        main_frame = Frame(self.root)
        main_frame.pack(padx=5, pady=5)
        # Left Frame for Image Preview (no fixed height)
        #self.preview_frame = Frame(main_frame, width=500)
        self.preview_frame = LabelFrame(main_frame, text="  预览图片文件",font=("微软雅黑", 12), width=400, height=460, padx=1, pady=1)
        self.preview_frame.grid(row=0, column=0, padx=10, pady=10)
        self.preview_frame.pack_propagate(False)
        # Image preview label
        self.image_label = Label(self.preview_frame)
        self.image_label.pack(padx=10, pady=10)
        # Frame for zoom and navigation buttons
        button_frame = Frame(self.preview_frame)
        button_frame.pack(side='bottom',pady=5)
        # Zoom buttons (Up and Down) and Navigation buttons (Previous and Next)
        self.zoom_in_button = Button(button_frame, text="放大", font=("微软雅黑", 12), command=self.zoom_in)
        self.zoom_in_button.pack(side=LEFT, padx=5)
        self.zoom_out_button = Button(button_frame, text="缩小", font=("微软雅黑", 12), command=self.zoom_out)
        self.zoom_out_button.pack(side=LEFT, padx=5)
        self.prev_button = Button(button_frame, text="上一张", font=("微软雅黑", 12), command=self.previous_image)
        self.prev_button.pack(side=LEFT, padx=5)
        self.next_button = Button(button_frame, text="下一张", font=("微软雅黑", 12), command=self.next_image)
        self.next_button.pack(side=LEFT, padx=5)
        # Right Frame for Text Display
        #self.text_frame = Frame(main_frame, width=400, height=300)
        self.text_frame = LabelFrame(main_frame, text="  提取信息显示 ", font=("微软雅黑", 12), width=400, height=300, padx=1, pady=1)
        self.text_frame.grid(row=0, column=1, padx=10, pady=10)
        # Text display area
        self.text_display = ScrolledText(self.text_frame, width=50, height=20, font=("微软雅黑", 12))
        self.text_display.pack(padx=5, pady=5)
        button_frame = Frame(self.root)
        button_frame.pack(expand=True)
        # Other buttons
        Button(button_frame, text="导入图片", font=("微软雅黑", 12), command=self.load_image).pack(side="left", padx=90, pady=10)
        Button(button_frame, text="提取信息", font=("微软雅黑", 12), command=self.start_evaluation).pack(side="left", padx=60, pady=10)
        Button(button_frame, text="打开目录", font=("微软雅黑", 12), command=self.open_output_dir).pack(side="left", padx=60, pady=10)
        Button(button_frame, text="复制信息", font=("微软雅黑", 12), command=self.copy_info).pack(side="left", padx=60, pady=10)
    def show_settings(self):
        self.settings_window = Toplevel(self.root)
        self.settings_window.attributes('-topmost', True)
        self.settings_window.title("豆包 API设置")
        Label(self.settings_window, text="请把kimi的API放在这里,使用ctrl+V:").pack(pady=5)
        self.api_var = StringVar()
        self.entry = Entry(self.settings_window, textvariable=self.api_var, width=30, font=("微软雅黑", 12))
        self.entry.pack()
        confirm_button = Button(self.settings_window, text="确认", command=lambda: self.apply_settings())
        confirm_button.pack(pady=10)
        screen_width = self.settings_window.winfo_screenwidth()
        screen_height = self.settings_window.winfo_screenheight()
        self.settings_window.update_idletasks()
        window_width = self.settings_window.winfo_width()
        window_height = self.settings_window.winfo_height()
        x_position = (screen_width - window_width) // 2
        y_position = (screen_height - window_height) // 2
        self.settings_window.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
    def apply_settings(self):
        new_time = self.api_var.get()
        self.api = new_time.strip()
        data = {'api': self.api}
        with open('config.json', 'w+') as f:
            json.dump(data, f, indent=4)
        self.settings_window.destroy()
    def copy_info(self):
        pyperclip.copy(self.text_display.get(1.0, END))
    def load_image(self):
        if len(str(self.api))  1:
            self.prev_button.config(state="normal" if self.current_image_index > 0 else "disabled")
            self.next_button.config(state="normal" if self.current_image_index  0:
            self.current_image_index -= 1
            self.image_path_entry.delete(0, END)
            self.image_path_entry.insert(0, self.image_files[self.current_image_index])
            self.info_var.set(f"已加载图片: {os.path.basename(self.image_files[self.current_image_index])} ({self.current_image_index + 1}/{len(self.image_files)})")
            self.current_scale = 1.0  # Reset zoom scale
            self.display_image(self.image_files[self.current_image_index])
            self.update_navigation_buttons()
    def next_image(self):
        if self.current_image_index = len(self.image_files):
            messagebox.showwarning("警告", "请先加载图片")
            return
        image_path = self.image_files[self.current_image_index]
        try:
            img = Image.open(image_path)
            # Estimate text area height (15 lines of font "微软雅黑" size 12)
            text_font = font.Font(family="微软雅黑", size=12)
            line_height = text_font.metrics("linespace")
            target_height = int(line_height * 15 * self.current_scale)
            # Calculate width based on aspect ratio
            original_width, original_height = img.size
            target_width = int(target_height * original_width / original_height)
            img = img.resize((target_width, target_height), Image.Resampling.LANCZOS)
            self.image_preview = ImageTk.PhotoImage(img)
            self.image_label.config(image=self.image_preview)
            # 更新路径栏显示当前图片(可选)
            self.image_path_entry.delete(0, END)
            self.image_path_entry.insert(0, image_path)
        except Exception as e:
            messagebox.showerror("错误", f"无法加载图片:{e}")
    def start_evaluation(self):
        threading.Thread(target=self.evaluate_essay).start()
    def evaluate_essay(self):
        if not self.image_files:
            messagebox.showerror("错误", "请先导入图片文件或文件夹!")
            return
        self.info_var.set("正在提取内容,请稍候...")
        try:
            for file in self.image_files:
                result = self.chat_doubao(file)
                base_name = os.path.splitext(os.path.basename(file))[0]
                output_path = os.path.join(self.output_dir, f"{base_name}.txt")
                with open(output_path, "a+", encoding="utf-8") as f:
                    f.write(result)
            self.info_var.set("提取完成!")
        except Exception as e:
            messagebox.showerror("错误", f"提取失败: {e}")
            self.info_var.set("提取失败,请重试")
    def get_order(self):
        with open("order.txt","r",encoding="utf-8") as f:
            order = f.read().strip()
        return order
    def image_to_base64(self,image_path):
        # 获取图片文件的MIME类型
        ext = os.path.splitext(image_path)[1].lower()
        mime_type = f"image/{ext[1:]}" if ext in ['.jpg', '.jpeg', '.png', '.gif'] else "image/jpeg"
        
        with open(image_path, "rb") as image_file:
            # 读取文件内容并进行Base64编码
            base64_data = base64.b64encode(image_file.read()).decode('utf-8')
            # 返回完整的data URI格式
            return f"data:{mime_type};base64,{base64_data}"
    def chat_doubao(self,local_image_path):
        # 初始化客户端
        client = OpenAI(
            base_url="https://ark.cn-beijing.volces.com/api/v3",
            api_key=""  # 这里要输入自己的API
        )
        try:
            # 转换本地图片为Base64编码
            image_data = self.image_to_base64(local_image_path)
            
            # 调用API处理图片
            response = client.chat.completions.create(
                model="doubao-seed-1-6-250615",
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "image_url",
                                "image_url": {
                                    "url": image_data  # 使用Base64编码的本地图片数据
                                },
                            },
                            {"type": "text", "text": self.get_order()},
                        ],
                    }
                ]
            )
            # 提取并打印内容
            content = response.choices[0].message.content
            self.text_display.insert(END, f"{content}\n")
            self.text_display.see(END)
            # 更新预览图片显示
            self.display_image(local_image_path)
            # 更新顶部路径栏显示
            self.image_path_entry.delete(0, END)
            self.image_path_entry.insert(0, local_image_path)
        except FileNotFoundError:
            self.text_display.insert('1.0',f"错误:找不到图片文件 '{local_image_path}'")
        except Exception as e:
            print(f"处理过程中发生错误:{str(e)}")
        return content
    def open_output_dir(self):
        os.startfile(self.output_dir)
if __name__ == "__main__":
    root = Tk()
    app = ImageEssayEvaluator(root)
    root.mainloop()
四、源码下载

批量图片信息提取_pythonfun作品.zip
(4.89 KB, 下载次数: 58)
2025-8-8 17:24 上传
点击文件名下载附件
下载积分: 吾爱币 -1 CB

微软, 图片

shiqiang   

这个工具怎么用,运行后窗口闪退?
alyWodeZH   

感谢大佬分享,这款真的很实用提取图片方便多了,比心比心❤️❤️
jiukou   

很实用的一个小工具。
638   

这个在什么环境中需要用到???
pythonfun
OP
  


638 发表于 2025-8-8 18:06
这个在什么环境中需要用到???

提取发票信息,批量提取同一类照片上的文字等,用处蛮多的,可以自定义指令。
huan12345   

可以用在工作室
Seyulio   

感谢分享
long760418   

点赞支持一个
sumrughNet   

谢谢你的分享,给一个点赞
您需要登录后才可以回帖 登录 | 立即注册

返回顶部