半自动图像裁剪工具(霍夫变换、QT)

基于霍夫变换识别图中圆形物体,并且识别不到的可以手动框选出来。

效果图如下:

初始界面:

点击Hough TransForm,红色圆圈表示霍夫圆检测的结果:

点击Choose Area,手动框选图片(绿色框):

点击Remake Choose  可以重置选择,点击 Save Choose可以保存选择的图像。

核心代码如下:

import os
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsScene, QGraphicsView, QPushButton, QVBoxLayout, QWidget, QFileDialog
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen
from PyQt5.QtCore import Qt, QPoint
import cv2
import numpy as np


class ImageCropper(QMainWindow):
    def __init__(self, image_path):
        super(ImageCropper, self).__init__()
        self.image_path = image_path
        self.cropping = False
        self.refPt = []
        self.cropped_regions = []
        self.circles = []
        self.load_image()
        self.view.mousePressEvent = self.mousePressEvent
        self.view.mouseReleaseEvent = self.mouseReleaseEvent
    def load_image(self):
        self.image_cv = cv2.imread(self.image_path)
        if self.image_cv is None:
            print("Error: Unable to load image.")
            return

        height, width, _ = self.image_cv.shape
        bytesPerLine = 3 * width
        qImg = QImage(self.image_cv.data, width, height, bytesPerLine, QImage.Format_RGB888).rgbSwapped()
        self.pixmap = QPixmap.fromImage(qImg)

        self.initUI()

    def initUI(self):
        self.setWindowTitle("Image Cropper")
        self.setGeometry(100, 100, 800, 600)

        self.scene = QGraphicsScene()
        self.scene.addPixmap(self.pixmap)

        self.view = QGraphicsView(self.scene)
        self.view.setFixedSize(600, 400)

        self.hfButton = QPushButton("Hough Transform")
        self.hfButton.clicked.connect(self.hough_transform)

        self.chooseButton = QPushButton("Choose Area")
        self.chooseButton.clicked.connect(self.choose_area)

        self.remakeButton = QPushButton("Remake Choose")
        self.remakeButton.clicked.connect(self.remake_choose)

        self.saveButton = QPushButton("Save Choose")
        self.saveButton.clicked.connect(self.save_choose)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addWidget(self.hfButton)
        layout.addWidget(self.chooseButton)
        layout.addWidget(self.remakeButton)
        layout.addWidget(self.saveButton)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

    def choose_area(self):
        self.cropping = True

    def remake_choose(self):
        self.cropping = False
        self.refPt = []
        self.cropped_regions = []
        self.circles = []
        self.scene.clear()
        self.scene.addPixmap(self.pixmap)

    def save_choose(self):
        if not self.cropped_regions and not self.circles:
            print("No regions to save.")
            return

        # 修改保存文件夹路径为 "cropped images" 文件夹内
        save_dir = os.path.join(os.getcwd(), "cropped images")
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        print(self.cropped_regions)

        for i, region in enumerate(self.cropped_regions):
            print(f"Processing region {i + 1}...")
            try:
                if len(region) == 2:
                    x1, y1 = region[0]
                    x2, y2 = region[1]
                    # 将坐标值转换为整数类型
                    x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
                    roi = self.image_cv[y1:y2, x1:x2]
                    file_name = f"cropped_{i + 1}.jpg"  # 根据索引设置文件名
                    file_path = os.path.join(save_dir, file_name)
                    cv2.imwrite(file_path, roi)
                    print(f"Saved image {file_name} successfully.")
                else:
                    print(f"Invalid region format for region {i + 1}.")
            except Exception as e:
                print(f"Error processing region {i + 1}: {e}")



        # 保存检测到的圆形图像及其周围的正方形区域
        for j, circle in enumerate(self.circles):
            try:
                if len(circle) == 2:
                    x1, y1 = circle[0]
                    x2, y2 = circle[1]
                    # 将坐标值转换为整数类型
                    x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
                    # 使用计算出的坐标来裁剪图像
                    roi_square = self.image_cv[y1:y2, x1:x2]
                    file_name_square = f"square_{j + 1}.jpg"  # 根据索引设置文件名
                    file_path_square = os.path.join(save_dir, file_name_square)
                    cv2.imwrite(file_path_square, roi_square)
                    print(f"Saved square image {file_name_square} successfully.")
                else:
                    print(f"Invalid circle format for circle {j + 1}.")
            except Exception as e:
                print(f"Error processing circle {j + 1}: {e}")

    def mousePressEvent(self, event):
        print("Mouse Press Event")
        if self.cropping:
            pos = event.pos()
            mapped_pos = self.view.mapToScene(pos)
            self.refPt = [(mapped_pos.x(), mapped_pos.y())]

    def mouseReleaseEvent(self, event):
        print("Mouse Release Event")
        if self.cropping:
            pos = event.pos()
            mapped_pos = self.view.mapToScene(pos)
            end_x, end_y = mapped_pos.x(), mapped_pos.y()
            length = max(abs(end_x - self.refPt[0][0]), abs(end_y - self.refPt[0][1]))
            start_x, start_y = self.refPt[0]
            if end_x < start_x:
                start_x = end_x
                end_x = self.refPt[0][0]
            if end_y < start_y:
                start_y = end_y
                end_y = self.refPt[0][1]
            self.cropped_regions.append([(start_x, start_y), (end_x, end_y)])
            self.draw_rectangle(start_x, start_y, end_x, end_y)

    def draw_rectangle(self, start_x, start_y, end_x, end_y):
        pen = QPen()
        pen.setColor(Qt.green)
        pen.setWidth(2)
        rect = self.scene.addRect(start_x, start_y, end_x - start_x, end_y - start_y, pen)

    def hough_transform(self):
        gray = cv2.cvtColor(self.image_cv, cv2.COLOR_BGR2GRAY)
        circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=8, param1=80, param2=40, minRadius=1, maxRadius=50)
        if circles is not None:
            circles = np.round(circles[0, :]).astype("int")
            for (x, y, r) in circles:
                self.circles.append([(x - r, y - r), (x + r, y + r)])
                self.draw_circle(x - r, y - r, x + r, y + r)
            print("Number of circles detected:", len(circles))
            print(self.circles)

    def draw_circle(self, x1, y1, x2, y2):
        pen = QPen()
        pen.setColor(Qt.red)
        pen.setWidth(2)
        circle = self.scene.addEllipse(x1, y1, x2 - x1, y2 - y1, pen)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    cropper = ImageCropper(r'C:\D\test\test2.jpg')
    cropper.show()
    sys.exit(app.exec_())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值