You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import shutil
import threading
import sys
import cv2
import numpy as np
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import os.path as osp
class MainWindow(QTabWidget):
def __init__(self):
# 初始化界面
super().__init__()
self.load_custom_font()
self.setWindowTitle('华东师范大学校园卡检测系统')
self.resize(1200, 800)
self.setWindowIcon(QIcon("images/UI/ECNU.png"))
# 图片读取进程
self.cab=None
self.stopEvent = threading.Event()
self.stopEvent.clear()
self.output_size = 1000
self.img2predict = ""
self.initUI()
self.webcam=False
self.reset_vid()
def load_custom_font(self):
# 加载自定义字体文件
font_id = QFontDatabase.addApplicationFont('font/OPPOSans-H-2.ttf')
if font_id != -1:
self.font_family = QFontDatabase.applicationFontFamilies(font_id)[0]
else:
self.font_family = '楷体' # 备用字体
def initUI(self):
# 图片检测子界面
font_title = QFont(self.font_family, 16)
font_main = QFont(self.font_family, 14)
# 图片识别界面, 两个按钮,上传图片和显示结果
img_detection_widget = QWidget()
img_detection_layout = QVBoxLayout()
img_detection_title = QLabel("图片检测功能")
img_detection_title.setFont(font_title)
mid_img_widget = QWidget()
mid_img_layout = QHBoxLayout()
self.left_img = QLabel()
self.right_img = QLabel()
self.left_img.setPixmap(QPixmap("images/UI/touxiang.png"))
self.right_img.setPixmap(QPixmap("images/UI/touxiang2.png"))
self.left_img.setAlignment(Qt.AlignCenter)
self.right_img.setAlignment(Qt.AlignCenter)
mid_img_layout.addWidget(self.left_img)
mid_img_layout.addStretch(0)
mid_img_layout.addWidget(self.right_img)
mid_img_widget.setLayout(mid_img_layout)
up_img_button = QPushButton("上传图片")
det_img_button = QPushButton("开始检测")
up_img_button.clicked.connect(self.upload_img)
det_img_button.clicked.connect(self.detect_img)
up_img_button.setFont(font_main)
det_img_button.setFont(font_main)
up_img_button.setStyleSheet("QPushButton{color:white}"
"QPushButton:hover{background-color: rgb(2,110,180);}"
"QPushButton{background-color:rgb(48,124,208)}"
"QPushButton{border:2px}"
"QPushButton{border-radius:5px}"
"QPushButton{padding:5px 5px}"
"QPushButton{margin:5px 5px}")
det_img_button.setStyleSheet("QPushButton{color:white}"
"QPushButton:hover{background-color: rgb(2,110,180);}"
"QPushButton{background-color:rgb(48,124,208)}"
"QPushButton{border:2px}"
"QPushButton{border-radius:5px}"
"QPushButton{padding:5px 5px}"
"QPushButton{margin:5px 5px}")
img_detection_layout.addWidget(img_detection_title, alignment=Qt.AlignCenter)
img_detection_layout.addWidget(mid_img_widget, alignment=Qt.AlignCenter)
img_detection_layout.addWidget(up_img_button)
img_detection_layout.addWidget(det_img_button)
img_detection_widget.setLayout(img_detection_layout)
# 视频识别界面
vid_detection_widget = QWidget()
vid_detection_layout = QVBoxLayout()
vid_title = QLabel("视频检测功能")
vid_title.setFont(font_title)
self.vid_img = QLabel()
self.vid_img.setPixmap(QPixmap("images/UI/logo.png"))
vid_title.setAlignment(Qt.AlignCenter)
self.vid_img.setAlignment(Qt.AlignCenter)
self.webcam_detection_btn = QPushButton("摄像头实时监测")
self.mp4_detection_btn = QPushButton("视频文件检测")
self.vid_stop_btn = QPushButton("停止检测")
self.webcam_detection_btn.setFont(font_main)
self.mp4_detection_btn.setFont(font_main)
self.vid_stop_btn.setFont(font_main)
self.webcam_detection_btn.setStyleSheet("QPushButton{color:white}"
"QPushButton:hover{background-color: rgb(2,110,180);}"
"QPushButton{background-color:rgb(48,124,208)}"
"QPushButton{border:2px}"
"QPushButton{border-radius:5px}"
"QPushButton{padding:5px 5px}"
"QPushButton{margin:5px 5px}")
self.mp4_detection_btn.setStyleSheet("QPushButton{color:white}"
"QPushButton:hover{background-color: rgb(2,110,180);}"
"QPushButton{background-color:rgb(48,124,208)}"
"QPushButton{border:2px}"
"QPushButton{border-radius:5px}"
"QPushButton{padding:5px 5px}"
"QPushButton{margin:5px 5px}")
self.vid_stop_btn.setStyleSheet("QPushButton{color:white}"
"QPushButton:hover{background-color: rgb(2,110,180);}"
"QPushButton{background-color:rgb(48,124,208)}"
"QPushButton{border:2px}"
"QPushButton{border-radius:5px}"
"QPushButton{padding:5px 5px}"
"QPushButton{margin:5px 5px}")
self.webcam_detection_btn.clicked.connect(self.open_cam)
self.mp4_detection_btn.clicked.connect(self.open_mp4)
self.vid_stop_btn.clicked.connect(self.close_vid)
# 添加组件到布局上
vid_detection_layout.addWidget(vid_title)
vid_detection_layout.addWidget(self.vid_img)
vid_detection_layout.addWidget(self.webcam_detection_btn)
vid_detection_layout.addWidget(self.mp4_detection_btn)
vid_detection_layout.addWidget(self.vid_stop_btn)
vid_detection_widget.setLayout(vid_detection_layout)
self.left_img.setAlignment(Qt.AlignCenter)
self.addTab(img_detection_widget, '图片检测')
self.addTab(vid_detection_widget, '视频检测')
self.setTabIcon(0, QIcon('images/UI/logo.png'))
self.setTabIcon(1, QIcon('images/UI/logo.png'))
# 上传图片
def upload_img(self):
fileName, fileType = QFileDialog.getOpenFileName(self, 'Choose file', '', '*.jpg *.png *.tif *.jpeg')
if fileName:
suffix = fileName.split(".")[-1]
save_path = osp.join("images/tmp", "original_image." + suffix)
shutil.copy(fileName, save_path)
# 应该调整一下图片的大小,然后统一放在一起
im0 = cv2.imread(save_path)
new_height = 400 * im0.shape[0] / im0.shape[1]
im0 = cv2.resize(im0, (400, int(new_height)), interpolation=cv2.INTER_CUBIC)
cv2.imwrite("images/tmp/compressed_image.jpg", im0)
self.img2predict = "images/tmp/compressed_image.jpg"
self.left_img.setPixmap(QPixmap("images/tmp/compressed_image.jpg"))
# 上传图片之后右侧的图片重置,
self.right_img.setPixmap(QPixmap("images/UI/touxiang2.png"))
def find_campus_card(self,bgr_image):
# 预处理图像,对图像进行压缩,便于之后处理
image_height, image_width = bgr_image.shape[:2]
# 掩膜匹配白色区域
hsv = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV)
lower_white = np.array([0, 0, 170])
upper_white = np.array([180, 80, 255])
mask = cv2.inRange(hsv, lower_white, upper_white)
# 开运算去除噪声
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# 高斯滤波,减少噪声和增强主要结构
blur_mask = cv2.GaussianBlur(mask, (3, 3), 0)
gradx = cv2.Sobel(blur_mask, cv2.CV_16SC1, 1, 0)
grady = cv2.Sobel(blur_mask, cv2.CV_16SC1, 0, 1)
# canny边缘检测
edges = cv2.Canny(gradx, grady, 50, 150)
contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_TC89_L1)
best_contour = None
best_rect = None
best_ratio_diff = float('inf')
desired_ratio = 2.5
min_area = 0.05 * image_width * image_height
for contour in contours:
epsilon = 0.02 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# 获取最小外接矩形
rect = cv2.minAreaRect(approx)
(width, height) = rect[1]
if width == 0 or height == 0:
continue
# 计算长宽比
if width < height:
ratio = height / width
else:
ratio = width / height
# 计算矩形面积
area = width * height
# 计算与期望比例的差异
ratio_diff = abs(ratio - desired_ratio)
# 更新最优轮廓
if ratio_diff < best_ratio_diff and area >= min_area:
best_contour = approx
best_rect = rect
best_ratio_diff = ratio_diff
if best_rect is not None:
# 获取最小外接矩形的顶点
box = cv2.boxPoints(best_rect)
box = np.intp(box)
# 在图像上绘制矩形
cv2.drawContours(bgr_image, [box], 0, (0, 0, 255), 2)
angle = best_rect[2]
if best_rect[1][0] < best_rect[1][1]:
angle -= 90
return bgr_image, box, angle
else:
return bgr_image, None,None
def background_remove(self,afterimg, box):
# 创建一个与原始图像大小相同的掩膜图像初始为全白255
mask = np.ones_like(afterimg) * 0
# 将矩形区域填充为黑色
cv2.drawContours(mask, [box], 0, (255, 255, 255), -1)
# 将掩膜图像与原始图像进行按位与操作,保留矩形区域为彩色,其他区域变为黑色
result = cv2.bitwise_and(afterimg, mask)
return result
def rotate_image(self,image, angle):
# 获取图像中心点
center = tuple(np.array(image.shape[1::-1]) / 2)
# 计算旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
# 进行仿射变换
rotated_image = cv2.warpAffine(image, rotation_matrix, image.shape[1::-1], flags=cv2.INTER_LINEAR)
return rotated_image
def detect_img(self):
source = self.img2predict
if source == "":
QMessageBox.warning(self, "请上传", "请先上传图片再进行检测")
else:
bgr_image = cv2.imread(source)
processed_image, box, rotation_angle = self.find_campus_card(bgr_image)
cv2.imwrite("images/tmp/find_card_image.jpg", processed_image)
if box is not None:
background_remove_img = self.background_remove(processed_image, box)
rotated_image = self.rotate_image(background_remove_img, rotation_angle)
cv2.imwrite("images/tmp/result_image.jpg", rotated_image)
self.left_img.setPixmap(QPixmap("images/tmp/find_card_image.jpg"))
self.right_img.setPixmap(QPixmap("images/tmp/result_image.jpg"))
else:
self.right_img.setPixmap(QPixmap("images/UI/error2.png"))
def open_cam(self):
self.webcam_detection_btn.setEnabled(False) # 禁用检测按钮
self.mp4_detection_btn.setEnabled(False) # 禁用 MP4 检测按钮
self.vid_stop_btn.setEnabled(True) # 启用停止按钮
self.cap = cv2.VideoCapture(0)
self.webcam=True
th = threading.Thread(target=self.process_video) # 创建一个新的线程
th.start() # 启动新线程
def open_mp4(self):
fileName, fileType = QFileDialog.getOpenFileName(self, 'Choose file', '', '*.mp4 *.avi *.mov')
if fileName:
self.webcam_detection_btn.setEnabled(False)
self.mp4_detection_btn.setEnabled(False)
self.vid_stop_btn.setEnabled(True) # 启用停止按钮
self.cap = cv2.VideoCapture(fileName)
th = threading.Thread(target=self.process_video) # 创建一个新的线程
th.start() # 启动新线程
def process_video(self):
while True:
ret, frame = self.cap.read()
if self.webcam:
frame = cv2.flip(frame, 1)
if ret:
new_height = 400 * frame.shape[0] / frame.shape[1]
frame = cv2.resize(frame, (400, int(new_height)), interpolation=cv2.INTER_CUBIC)
processed_image, box, rotation_angle = self.find_campus_card(frame)
cv2.imwrite("images/tmp/processed_video_image.jpg", processed_image)
self.vid_img.setPixmap(QPixmap("images/tmp/processed_video_image.jpg"))
if self.stopEvent.is_set() == True:
self.stopEvent.clear()
break
def closeEvent(self, event):
reply = QMessageBox.question(self,'退出',"是否退出?",QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
if reply == QMessageBox.Yes:
self.close()
event.accept()
else:
event.ignore()
# 界面重置
def reset_vid(self):
self.webcam=False
self.webcam_detection_btn.setEnabled(True)
self.mp4_detection_btn.setEnabled(True)
self.vid_img.setPixmap(QPixmap("images/UI/logo.png"))
# 视频重置
def close_vid(self):
if self.cap:
self.cap.release()
self.stopEvent.set()
self.reset_vid()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())