基于霍夫变换识别图中圆形物体,并且识别不到的可以手动框选出来。
效果图如下:
初始界面:
点击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_())