Loading

图像识别第一版v1【ocr】

import os
import sys
from PyQt5.QtWidgets import (
    QApplication,      # 应用主类,管理事件循环
    QMainWindow,       # 主窗口框架(带菜单栏/状态栏)
    QWidget,           # 基础空白控件(所有可视组件的基类)
    QVBoxLayout,       # 垂直布局管理器
    QHBoxLayout,       # 水平布局管理器
    QPushButton,       # 按钮控件
    QLabel,            # 文本/图片显示标签
    QFileDialog,       # 文件选择对话框
    QMessageBox        # 消息提示框
)
from PyQt5.QtGui import (
    QPixmap,  # 图像显示载体(优化显示的图像类)
    QPainter,  # 绘图工具(可在控件上绘制图形)
    QPen,  # 画笔(设置线条颜色/粗细)
    QImage, # 图像数据容器(像素级操作)
    QColor
)
from PyQt5.QtCore import (
    Qt,        # 包含枚举常量(如对齐方式、鼠标按键)
    QPoint,    # 二维坐标点(x,y)
    QRect      # 矩形区域(x,y,width,height)
)
import requests

class OCRImage(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("图片标注工具")
        self.setGeometry(100, 100, 800, 600)

        # 初始化变量
        self.image_path = None
        self.start_point = QPoint()               # 绘制方框时的起始点和结束点
        self.end_point = QPoint()                 # 绘制方框时的起始点和结束点
        self.drawing = False
        self.rectangles = []                      # 存储所有方框

        # 初始化UI
        self.init_ui()

    def init_ui(self):
        # 主窗口部件
        central_widget = QWidget()                          # 它将作为主窗口的中心部件,用于容纳其他控件。
        self.setCentralWidget(central_widget)               # 将 central_widget 设置为当前主窗口的中心部件。在 QMainWindow 中,中心部件是用来显示主要内容的区域。
        main_layout = QVBoxLayout(central_widget)           # 创建了一个垂直布局管理器 main_layout,并将其应用到 central_widget 上。这意味着后续添加到 central_widget 中的子控件会按照垂直方向依次排列。

        # 按钮区域
        btn_layout = QHBoxLayout()                          # 创建了一个水平布局管理器 btn_layout,用于管理按钮的布局。后续添加到这个布局中的按钮会按照水平方向依次排列。

        self.btn_open = QPushButton("打开图片")              # 创建按钮
        self.btn_open.clicked.connect(self.open_image)      # 绑定方法
        btn_layout.addWidget(self.btn_open)                 # 将按钮添加到水平布局 btn_layout 中。

        self.btn_upload = QPushButton("上传图片")
        self.btn_upload.setEnabled(False)
        self.btn_upload.clicked.connect(self.upload_image)
        btn_layout.addWidget(self.btn_upload)

        self.btn_clear = QPushButton("清除方框")
        self.btn_clear.setEnabled(False)
        self.btn_clear.clicked.connect(self.clear_rectangles)
        btn_layout.addWidget(self.btn_clear)

        main_layout.addLayout(btn_layout)                               # 将水平布局 btn_layout 添加到垂直布局 main_layout 中。

        # 图片显示区域
        self.lbl_image = QLabel()                                       # 创建一个 QLabel 类的实例 self.lbl_image。QLabel 是 PyQt5 中用于显示文本或图像的控件。在这个应用场景中,它将用于显示图片。
        self.lbl_image.setAlignment(Qt.AlignCenter)                     # 设置 QLabel 内内容的对齐方式为居中对齐。Qt.AlignCenter 是 PyQt5 中定义的一个常量,表示居中对齐。这样,当在 QLabel 中显示图片时,图片会在 QLabel 区域内居中显示。
        self.lbl_image.setStyleSheet("background-color: #333;")         # 设置样式表。这里设置了 QLabel 的背景颜色为 #333(深灰色)。样式表是一种用于设置控件外观的机制,类似于 CSS 在网页中的作用。
        self.lbl_image.mousePressEvent = self.mouse_press               # 鼠标按下事件
        self.lbl_image.mouseMoveEvent = self.mouse_move                 # 鼠标移动事件
        self.lbl_image.mouseReleaseEvent = self.mouse_release           # 鼠标释放事件
        main_layout.addWidget(self.lbl_image)                           # 将QLabel 添加到垂直布局 main_layout 中。

    def open_image(self):
        """ 打开图片 """
        file_path, _ = QFileDialog.getOpenFileName(                 # 用于弹出文件选择对话框,让用户选择一个文件。
            self,                          # 表示对话框的父窗口,这里通常是当前的主窗口。
            "选择图片",                      # 说明文字
            "D:\Desktop",                            # 对话框打开时默认显示的路径
            "图片文件 (*.jpg *.jpeg *.png)"  # 文件过滤器,用于限制用户只能选择指定类型的文件,这里只允许选择 JPEG 和 PNG 格式的图片文件。
        )

        if file_path:
            self.image_path = file_path
            self.rectangles = []  # 清除之前的方框
            self.load_image()
            self.btn_upload.setEnabled(True)
            self.btn_clear.setEnabled(True)

    def load_image(self):
        """ 加载图片,展示 """
        self.pixmap = QPixmap(self.image_path)          # 加载各种格式的图片文件。
        self.scaled_pixmap = self.pixmap.scaled(        # 用于对图片进行缩放。
            self.lbl_image.size(),   # 表示 QLabel 控件的当前大小,即图片将被缩放到适合 QLabel 控件的大小。
            Qt.KeepAspectRatio,      # 表示在缩放图片时保持图片的原始宽高比,避免图片变形。
            Qt.SmoothTransformation  # 是一个转换模式的标志,表示使用平滑的转换算法进行缩放,这样可以使缩放后的图片更加清晰。
        )
        self.lbl_image.setPixmap(self.scaled_pixmap)    # 将指定的 QPixmap 对象显示在 QLabel 控件上。
        self.update()

    def mouse_press(self, event):
        """ 鼠标点击事件 """
        # 处理鼠标按下事件,当用户按下鼠标左键且已经加载了图片时,开始绘制矩形框。
        if event.button() == Qt.LeftButton and self.image_path:
            self.drawing = True                 # 标记开始绘制矩形框。
            self.start_point = event.pos()      # 将矩形框的起始点和结束点都设置为当前鼠标位置。
            self.end_point = event.pos()

    def mouse_move(self, event):
        """ 鼠标移动事件 """
        if self.drawing and self.image_path:
            self.end_point = event.pos()
            self.update_image_with_rect()

    def mouse_release(self, event):
        if event.button() == Qt.LeftButton and self.drawing:
            self.drawing = False
            # 将方框添加到列表
            rect = QRect(self.start_point, self.end_point).normalized()
            self.rectangles.append(rect)
            self.update_image_with_rect()

    def update_image_with_rect(self):
        """ 主要功能是在图片上绘制矩形框(标注),并将绘制好的图片显示在 QLabel 控件上。 """

        # 创建临时图片用于绘制
        temp_pixmap = self.scaled_pixmap.copy()         # 创建副本,避免直接在原始图片上进行绘制,从而保持原始图片的完整性。
        painter = QPainter(temp_pixmap)                 # 创建一个 QPainter 对象 painter,用于在 temp_pixmap 上进行绘制操作。
        painter.setPen(QPen(QColor(148, 0, 211), 2, Qt.SolidLine))   # 设置绘制矩形框时使用的画笔。Qt.red 表示画笔的颜色为红色,2 表示画笔的宽度为 2 像素,Qt.SolidLine 表示画笔的样式为实线。

        # 绘制所有已保存的方框
        for rect in self.rectangles:
            painter.drawRect(rect)

        # 绘制当前正在画的方框
        if self.drawing:
            current_rect = QRect(self.start_point, self.end_point).normalized()   # 如果正在绘制矩形框,根据当前的起始点 self.start_point 和结束点 self.end_point 创建一个 QRect 对象 current_rect,并使用 normalized 方法确保矩形框的宽度和高度为正值。
            painter.drawRect(current_rect)                                        # 在 temp_pixmap 上绘制当前正在绘制的矩形框。

        painter.end()                                   # 结束 QPainter 的绘制操作,释放相关资源。
        self.lbl_image.setPixmap(temp_pixmap)           # 将绘制好的 temp_pixmap 设置为 self.lbl_image 的显示内容,从而更新界面上显示的图片。

    def clear_rectangles(self):
        self.rectangles = []
        self.load_image()  # 重新加载原始图片

    def upload_image(self):
        if not self.image_path:
            return

        # 1. 在原图上绘制方框
        original_image = QImage(self.image_path)        # 依据 self.image_path 加载原始图片。
        painter = QPainter(original_image)              # 创建 QPainter 对象,用于在原始图片上绘制标注框。
        painter.setPen(QPen(QColor(148, 0, 211), 5, Qt.SolidLine))   # 设置画笔,颜色为红色,宽度为 5 像素,样式为实线。

        # 计算方框在原始图片上的位置(考虑缩放比例)
        scale_x = self.pixmap.width() / self.scaled_pixmap.width()
        scale_y = self.pixmap.height() / self.scaled_pixmap.height()

        for rect in self.rectangles:
            # 转换坐标到原始图片尺寸
            original_rect = QRect(
                int(rect.x() * scale_x),
                int(rect.y() * scale_y),
                int(rect.width() * scale_x),
                int(rect.height() * scale_y)
            )
            painter.drawRect(original_rect)             # 绘制到原始图片上。
        painter.end()                                   # 结束绘制操作。

        # 2. 保存临时文件
        curr_path = __file__             # 当前文件路径
        base_dir = os.path.dirname(os.path.dirname(curr_path))
        temp_path = os.path.join(base_dir, 'media', 'temp', 'img', self.image_path.split("/")[-1])
        original_image.save(temp_path)

        # 3. 模拟上传(替换为你的真实API)
        try:
            # 示例:使用requests上传
            url = "http://127.0.0.1:8000/api/ocr/img/"
            files = {'plane': open(temp_path, 'rb')}
            response = requests.post(url, files=files)

            QMessageBox.information(
                self,
                "上传成功",
                f"文件已上传!\n服务器返回: {response.text}"
            )
        except Exception as e:
            QMessageBox.critical(
                self,
                "上传失败",
                f"上传出错: {str(e)}"
            )

    def resizeEvent(self, event):
        if hasattr(self, 'pixmap') and self.pixmap:
            self.load_image()  # 窗口大小改变时重新缩放图片
posted @ 2025-04-30 17:06  一只大学生  阅读(8)  评论(0)    收藏  举报