PyQT5制作轮廓检测软件

103 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

轮廓检测常用于瑕疵检测的某些环节中,在图像处理中有着非常重要的作用。目前Opencv已经提供了瑕疵检测的一些函数,之前cv2.contours()函数就能完成这一系列的操作,目前opencv对该函数做了升级,升级后的函数为cv2.connectedComponentsWithStats(),提供了非常全面的功能,例如可以帮助计算轮廓的周长、面积等。

为了方便直观的查看计算结果,我使用PyQT5制作了一个简单的界面,界面的UI代码如下

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'logUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(702, 579)
        self.horizontalLayoutWidget = QtWidgets.QWidget(Form)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 681, 41))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton_2 = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.horizontalLayout.addWidget(self.pushButton_2)
        self.pushButton = QtWidgets.QPushButton(self.horizontalLayoutWidget)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.verticalLayoutWidget = QtWidgets.QWidget(Form)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 50, 681, 521))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(self.verticalLayoutWidget)
        self.label.setText("")
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)

        self.retranslateUi(Form)
        self.pushButton_2.clicked.connect(Form.openImg) # type: ignore
        self.pushButton.clicked.connect(Form.detect) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton_2.setText(_translate("Form", "打开图像"))
        self.pushButton.setText(_translate("Form", "轮廓检测"))

UI的界面如下

image.png

然后就是对两个按钮执行的槽函数操作,首先打开一幅图像,记录图像的绝对地址,然后将图像的绝对地址传递给检测函数,检测函数获取到图像时并对图像进行检测。

此处需要说明的是,对检测按钮并没有进行图像地址的判断,如果不先通过打开图像获取到一副待检测图像,直接点击轮廓检测按钮时会出现报错终止的情况,只需要在轮廓检测按钮的槽函数内加一个if判断,判断self.file_path[0][0]是否为None,如果不为None时进行检测,否则的话不作处理,直接pass。

from logUI import Ui_Form
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel
from PyQt5.QtWidgets import QFileDialog
import sys
import cv2
from PyQt5.QtGui import *
import calGrade
import calROIGrade
from PyQt5.QtCore import QRect, Qt
import numpy as np

class MainWindow(QMainWindow, Ui_Form):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.setupUi(self)
        self.file_path = None


    def Defect_Marking(self, src):
        gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
        ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)  # 二值化
        num_labels, labels, stats, centers = cv2.connectedComponentsWithStats(binary, connectivity=8, ltype=cv2.CV_32S)
        colors = []
        # 生成随机颜色
        for i in range(num_labels):
            b = np.random.randint(0, 256)
            g = np.random.randint(0, 256)
            r = np.random.randint(0, 256)
            colors.append((b, g, r))
        colors[0] = (0, 0, 0)
        image = np.copy(src)
        dist = []
        areas = []
        for t in range(1, num_labels, 1):
            x, y, w, h, area = stats[t]
            cx, cy = centers[t]
            cv2.circle(image, (np.int32(cx), np.int32(cy)), 2, (0, 255, 0), 2, 8, 0)
            cv2.rectangle(image, (x, y), (x + w, y + h), colors[t], 1, 8, 0)
            cv2.putText(image, "num:" + str(t), (x, y), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 0, 255), 1)
            print("label index %d, area of the label : %d" % (t, area))
            dist.append([x, y, x + w, y + h])
            areas.append(area)
        print("total: ", num_labels - 1)
        return image

    def openImg(self):
        try:
            self.file_path = QFileDialog.getOpenFileNames(self, "select file", "./", "*.*")
        except Exception as e:
            print(e)
        if self.file_path is not None:
            img1 = cv2.imread(self.file_path[0][0])
            img_dis = QImage(img1, img1.shape[1], img1.shape[0], QImage.Format_BGR888)
            # 加载图片,并设定图片大小
            img = QPixmap(img_dis).scaled(int(img1.shape[1]), int(img1.shape[0]))
            width = img.width()  ##获取图片宽度
            height = img.height()  ##获取图片高度
            if width / self.label.width() >= height / self.label.height():  ##比较图片宽度与label宽度之比和图片高度与label高度之比
                ratio = width / self.label.width()
            else:
                ratio = height / self.label.height()
            new_width = int(width / ratio)  ##定义新图片的宽和高
            new_height = int(height / ratio)
            new_img = img.scaled(new_width, new_height)  ##调整图片尺寸
            # img_dis = QPixmap(img_dis).scaled(int(img.shape[1]), int(img.shape[0]))
            self.label.setPixmap(new_img)

    def detect(self):
        img1 = cv2.imread(self.file_path[0][0])
        img = self.Defect_Marking(img1)
        img_dis = QImage(img, img.shape[1], img.shape[0], QImage.Format_BGR888)
        # 加载图片,并设定图片大小
        img = QPixmap(img_dis).scaled(int(img.shape[1]), int(img.shape[0]))
        width = img.width()  ##获取图片宽度
        height = img.height()  ##获取图片高度
        if width / self.label.width() >= height / self.label.height():  ##比较图片宽度与label宽度之比和图片高度与label高度之比
            ratio = width / self.label.width()
        else:
            ratio = height / self.label.height()
        new_width = int(width / ratio)  ##定义新图片的宽和高
        new_height = int(height / ratio)
        new_img = img.scaled(new_width, new_height)  ##调整图片尺寸
        # img_dis = QPixmap(img_dis).scaled(int(img.shape[1]), int(img.shape[0]))
        self.label.setPixmap(new_img)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

首先,我先把实验用的原图放上

rice.jpg

然后就是程序的运行结果,打开一幅图像查看原图

image.png

然后是执行轮廓检测的结果

image.png

为了区分个体,对不同的轮廓用不同的颜色进行标记,最终的检测结果如上图所示。