基于pyqt的图像处理界面设计

以下是一个小白因为某个原因而学习pyqt的学习结果

首先是环境(因为python的环境问题,花了不少时间,着实头痛了一阵,所以把各种版本写在前面,总之逢山开路遇水搭桥,度娘解决一切)

使用python3.9,pyqt5,opencv-python 4.6.0.66,其余环境就隐去不表

安装pyqt5以及相关配置、设置外部工具(qt designer以及pyuic)等等过程请自行百度,这里不再详述,直接开始界面的设计。

新建一个.ui文件

 从左侧拉出Label组件作为显示图像的工具,再拉出Tab Widget,在其中放入几个Buttom按钮,增加Tab Widget的页数(组件右键->插入页),添加菜单栏(最上方“在这里输入”处双击,更改文字内容后回车)

按照以上步骤设计一些按钮以及菜单栏,设计完成如下图:

 (“文件”下有两个子菜单“打开”以及“保存”)

以下进行程序的编辑

首先,使按钮和自定义的函数链接起来,使用槽函数实现。

点击Qt Designer的"编辑"->"编辑信号/槽"

长按一个Buttom,形成如下效果

 会弹出如下弹窗

 点击左栏的clicked(),点击右栏下面的“编辑”,自定义一个函数名

在上栏中点击加号,自定义一个函数名称,如下图自定义一个函数为“pic_show()”

 将所有按钮的自定义函数都完成之后,进行保存。

这里特别说明菜单栏添加槽函数,可以双击已经添加了的自定义函数,调出“配置连接”界面,按照上面所说自定义一个函数,然后在右下角的“信号/槽 编辑器”页面中,“发送者”选择菜单按键的名称,“信号”选择triggered(),“接收者”选择MainWindow,“槽”选择刚刚自定义好的函数名。事实上,所有的按钮都可以采用以上过程进行信号和槽函数的连接,这里不再赘述。

全部完成后如下图

 右上角保存这个.ui,然后关闭Qt Designer。如果想再次进入这个界面编辑ui界面,可以右键项目中的.ui文件,选择外部工具中添加好的Qt。

下面将.ui文件翻译成.py文件。右键项目中的.ui文件,选择添加好的外部工具pyuic(这里注意,如果配置好的python环境使用PyQt5,就请也使用pyuic5,不要用pyuic6,否则编译程序时会产生错误。我自己做的时候就这样两掺了,结果还是乖乖的改成pyuic5了,qt设计时Qt Designer是否也必须用5我不确定,我自己好像就是用的6里面带的)。

翻译好后,项目中会出现一个和.ui文件名字一样的.py文件,这样就可以进行下一步具体程序的编写了。

为确保之后的书写中不会遗漏import的内容,这里把所有用到的都写在前面。

import numpy as np
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
from PIL import ImageEnhance
from PIL import ImageQt
import os
import cv2

如果想要运行编写的界面,需要添加主函数,直接写在最后就行,注意不要写到类里面去

import sys

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(mainWindow)
    mainWindow.show()
    sys.exit(app.exec_())

添加好主函数可以运行一下看看有没有错误,运行结果如下图

 与设计好的一样。

下面需要对翻译好的内容做一些更改,找到所有

self.某按键或菜单名称.clicked.connect(MainWindow.自定义函数名)

将其中的MainWindow改为self,如果使用pycharm,此时“self”应该变为紫色

下面编写载入图像的函数,对应的按钮是菜单栏中的“打开”,程序如下

def pic_show(self):
    # 打开文件路径
    # 设置文件扩展名过滤,注意用双分号间隔
    imgName, imgType = QFileDialog.getOpenFileName(None, "打开图片", "",
                                                   " *.jpg;;*.png;;*.jpeg;;*.bmp;;All Files (*)")
    print(imgName)
    self.img_path = imgName
    image = QtGui.QPixmap(imgName)
    self.befow.setPixmap(image)

这个函数可以由使用者自己去选择图片所在路径,打开后如下图

 为了实现如上图中,图片自动适应框的大小,需要对Label组件进行设置。打开Label的属性编辑器(在Qt的右侧栏)

 勾选其中的scaledContents。然后保存、重新pyuic。。。。(是的,刚刚改写和添加的程序都白干了,请从头再来一遍,原本想把这个事情放在文章的最后说的,想来想去还是不要这么跳脱了hhhh。这个事情也告诉我们,写好的程序最好备份一下,修改ui之后可以直接复制过去。)

下面来写一个保存处理好的图片的函数,对应按键为菜单栏中的“保存”

def pic_save(self):
    img = self.pp
    name_str = self.name
    print(name_str)
    if name_str == 'F:/change2.jpg':
        img.save(name_str)
    else:
        cv2.imwrite(name_str, img)
    print("已保存")

这里的self.name是一个定义在类内的变量,储存 一个地址,用于储存图片。self.pp储存的是处理好的图片。下面的if语句是因为后面的处理方式不同,采取的保存方式也不同(本质上就是格式的问题,想要改变应该添加格式转换程序就可以)

下面是直方图均衡化的程序

def pic_change1(self):
    imgpt = self.img_path
    print(imgpt)
    img = cv2.imread(imgpt)
    yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
    yuv[:, :, 0] = cv2.equalizeHist(yuv[:, :, 0])
    output = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR)
    cv2.imwrite("F:/12.jpg", output)
    image3 = QtGui.QPixmap(r"F:/12.jpg")
    self.dis(image3)
    self.pp = output
    self.name = 'F:/change1.jpg'
    print('直方图均衡化')
    os.remove(r"F:/12.jpg")

这个程序当中存在一个问题,正如程序中所写的,采取的是将处理好的图片使用opencv库中的imwrite函数存入电脑,然后使用Qt中的QtGui.QPixmap函数读取这张图片并显示,最后再删除这张图片的方式,简言之就是饶了好大一个圈子。究其原因依旧是图片格式的问题,cv2处理的图片采用的是Image格式,而Qt的显示需要QPixmap格式,事实上是存在格式转换函数的:

ImageQt.toqpixmap

(另附一些图片格式转换的函数:PIL.Image 、QImage和QPixmap的相互转化_JlexZzzz的博客-CSDN博客_qpixmap转qimage

但不知为何,使用这个函数并没有成功,或者是我使用的方法有问题,鉴于时间缘故只能采取一种舍近求远的方式解决问题。之后各个函数都使用这样的方式,之后就不再赘述,等未来有时间再回过头来好好研究一下问题出在哪。

直方图均衡化的结果如下

 

再下面是对比度增强的函数

    def pic_change2(self):
        img = self.img_path
        print(img)
        image = QtGui.QPixmap(img)
        image2 = ImageQt.fromqimage(image)
        imgg = ImageEnhance.Contrast(image2).enhance(2)
        imgg.save(r"F:\\文件\\11.jpg")
        image3 = QtGui.QPixmap(r"F:\\文件\\11.jpg")
        self.dis(image3)
        self.pp = imgg
        self.name = 'F:/change2.jpg'
        print('对比度增强')
        os.remove(r"F:\\文件\\11.jpg")

结果如下:

接下来是均值滤波

    def pic_change3(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        avg = cv2.blur(img, (3, 3))
        cv2.imwrite("F:/12.jpg", avg)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = avg
        self.name = 'F:/change3.jpg'
        print('均值滤波')
        os.remove(r"F:/12.jpg")

结果如下

在下面是中值滤波

    def pic_change4(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        mid = cv2.medianBlur(img, 5)
        cv2.imwrite("F:/12.jpg", mid)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = mid
        self.name = 'F:/change4.jpg'
        print('中值滤波')
        os.remove(r"F:/12.jpg")

结果如下

接下来是高斯滤波

    def pic_change5(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        guass = cv2.GaussianBlur(img, (15, 15), 0)
        cv2.imwrite("F:/12.jpg", guass)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = guass
        self.name = 'F:/change5.jpg'
        print('高斯滤波')
        os.remove(r"F:/12.jpg")

结果如下

正如三个滤波的结果所显示的,效果并不明显,应该是代码中相关参数调节的问题,留到之后有时间再调吧。

下面是图像分割部分,首先是Canny算子

    def pic_change6(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        grarimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        gauss = cv2.GaussianBlur(grarimg, (3, 3), 0)
        canny = cv2.Canny(gauss, 50, 150)
        cv2.imwrite("F:/12.jpg", canny)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = canny
        self.name = 'F:/change6.jpg'
        print('图像分割:Canny')
        os.remove(r"F:/12.jpg")

结果如下

下面是Roberts算子

    def pic_change7(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        grarimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 算子
        kkx = np.array([[-1, 0], [0, 1]], dtype=int)
        kky = np.array([[0, -1], [1, 0]], dtype=int)
        x = cv2.filter2D(grarimg, cv2.CV_16S, kkx)
        y = cv2.filter2D(grarimg, cv2.CV_16S, kky)

        absx = cv2.convertScaleAbs(x)
        absy = cv2.convertScaleAbs(y)
        outputimg = cv2.addWeighted(absx, 0.5, absy, 0.5, 0)
        cv2.imwrite("F:/12.jpg", outputimg)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = outputimg
        self.name = 'F:/change7.jpg'
        print('图像分割:Roberts')
        os.remove(r"F:/12.jpg")

结果如下

 接下来是Sobel算子

 def pic_change8(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        grarimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 算子
        x = cv2.Sobel(grarimg, cv2.CV_16S, 1, 0)
        y = cv2.Sobel(grarimg, cv2.CV_16S, 0, 1)
        absx = cv2.convertScaleAbs(x)
        absy = cv2.convertScaleAbs(y)
        outputimg = cv2.addWeighted(absx, 0.5, absy, 0.5, 0)
        cv2.imwrite("F:/12.jpg", outputimg)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = outputimg
        self.name = 'F:/change8.jpg'
        print('图像分割:Sobel')
        os.remove(r"F:/12.jpg")

结果如下

下面是Prewitt算子

    def pic_change9(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        grarimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 算子
        kkx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
        kky = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
        x = cv2.filter2D(grarimg, cv2.CV_16S, kkx)
        y = cv2.filter2D(grarimg, cv2.CV_16S, kky)

        absx = cv2.convertScaleAbs(x)
        absy = cv2.convertScaleAbs(y)
        outputimg = cv2.addWeighted(absx, 0.5, absy, 0.5, 0)
        cv2.imwrite("F:/12.jpg", outputimg)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = outputimg
        self.name = 'F:/change9.jpg'
        print('图像分割:Prewitt')
        os.remove(r"F:/12.jpg")

结果如下

最后是分水岭算法

    def pic_change10(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        grarimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        ret, binary = cv2.threshold(grarimg, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

        contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        cv2.drawContours(img, contours, -1, (0, 255, 0), 1)

        cv2.imwrite("F:/12.jpg", img)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = img
        self.name = 'F:/change10.jpg'
        print('图像分割:分水岭')
        os.remove(r"F:/12.jpg")

结果如下

 与滤波算法相同,分割的效果有些不尽如人意,具体参数的调节留后再补

后面是形态学相关的内容,首先是腐蚀

    def pic_change11(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        kernel = np.ones((5, 5), np.uint8)

        fs = cv2.erode(img, kernel)
        cv2.imwrite("F:/12.jpg", fs)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = fs
        self.name = 'F:/change11.jpg'
        print('腐蚀')
        os.remove(r"F:/12.jpg")

结果如下

然后是膨胀

 def pic_change12(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        kernel = np.ones((5, 5), np.uint8)

        fs = cv2.dilate(img, kernel)
        cv2.imwrite("F:/12.jpg", fs)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = fs
        self.name = 'F:/change12.jpg'
        print('膨胀')
        os.remove(r"F:/12.jpg")

结果如下

接下来是高帽

    def pic_change13(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        kernel = np.ones((5, 5), np.uint8)

        fs = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
        cv2.imwrite("F:/12.jpg", fs)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = fs
        self.name = 'F:/change13.jpg'
        print('高帽')
        os.remove(r"F:/12.jpg")

结果如下

最后是低帽

    def pic_change14(self):
        imgpt = self.img_path
        print(imgpt)
        img = cv2.imread(imgpt)
        kernel = np.ones((5, 5), np.uint8)
        fs = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
        cv2.imwrite("F:/12.jpg", fs)
        image3 = QtGui.QPixmap(r"F:/12.jpg")
        self.dis(image3)
        self.pp = fs
        self.name = 'F:/change14.jpg'
        print('低帽')
        os.remove(r"F:/12.jpg")

结果如下

最后一个函数程序,是对应于四个“原图”按钮的,用来显示载入的原图

    def pic_show2(self):
        print('原图')
        imgName=self.img_path
        image = QtGui.QPixmap(imgName)
        self.befow.setPixmap(image)

以上函数中的self.dis()函数是用来显示处理后的函数,具体如下

    def dis(self, img2):
        self.befow.setPixmap(img2)

现存的问题:

1.主函数和其他内容的分离。

2.猜想应该有不需要每次修改.ui文件就要把一堆函数重新复制一遍的方法。

3.存储图片的名字现在仍为“changeX.jpg”,如果在路径中改为中文名称,则最终存储的名称为中文乱码,猜测可能是因为opencv不能接受中文路径的原因。

4.格式转换

5.各个程序的具体参数调节

以上问题留待再次需要pyqt时再细想,历时4天的研究成果自此结束(一半时间都在安装环境以及解决版本等等问题,简直太折磨了)

卿某

2022.9.27

物联沃分享整理
物联沃-IOTWORD物联网 » 基于pyqt的图像处理界面设计

发表评论