技术标签: python 图像处理 # OpenCV 实战项目 pyqt GUI opencv
欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制
OpenCV-PyQT项目实战(4)OpenCV 与PyQt的图像转换
OpenCV-PyQT项目实战(5)项目案例01:图像模糊
OpenCV-PyQT项目实战(6)项目案例02:滚动条应用
OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
OpenCV-PyQT项目实战(8)项目案例04:鼠标定位
OpenCV-PyQT项目实战(9)项目案例04:视频播放
OpenCV-PyQT项目实战(10)项目案例06:键盘事件与视频抓拍
OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频
本节介绍 OpenCV 与PyQt的图像格式及转换。
在OpenCV-PyQt的项目中,通常使用OpenCV读写和处理图像,使用PyQt进行显示和交互。但是,OpenCV与PyQt中的图像存储格式不同,需要进行转换。这里有不少坑,小心行驶。
数字图像由像素点组成的矩阵来描述,以多维数据集来表示和处理。
OpenCV的Python API是基于Numpy库来存储和处理多维数组,图像的数据结构是ndarray多维数组。OpenCV中对图像的任何操作,本质上都是对Numpy多维数组的操作和运算。
OpenCV 中的二值图像和灰度图像用二维数组 (h, w) 表示,数组中的每个元素表示对应一个像素的灰度,每个像素的位深度为 8位。
OpenCV 中二值图像被作为特殊的灰度图像,每个像素点的值为 0(黑色)或 255(白色)。
OpenCV 中的彩色图像用三维数组 (h, w, ch=3) 表示,数组中的每个元素对应一个像素的某种颜色分量,每个像素的位深度为 24位。
OpenCV 使用 BGR 格式,色彩通道顺序为 B/G/R,因此 B 通道是 img[:, :, 0], G 通道是 img[:, :, 1], R 通道是 img[:, :, 2]。
OpenCV 中图像对象的数据结构是 ndarray 多维数组,因此 ndarray 数组的属性和操作方法也都适用于 OpenCV 的图像对象。例如:
img.ndim:查看图像的维数,彩色图像的维数为 3,灰度图像的维数为 2。
img.shape:查看图像的形状,即图像栅格的行数(高度)、列数(宽度)、通道数。
img.size:查看图像数组元素总数,灰度图像的数组元素总数为像素数量,彩色图像的数组元素总数为像素数量与通道数的乘积。
# 图像数组的属性
imgFile = "../images/imgLena.tif" # 读取文件的路径
img1 = cv2.imread(imgFile, flags=1) # flags=1 读取彩色图像(BGR)
img2 = cv2.imread(imgFile, flags=0) # flags=0 读取为灰度图像
# cv2.imshow("Demo1", img1) # 在窗口显示图像
# key = cv2.waitKey(0) # 等待按键命令
# 维数(ndim), 形状(shape), 元素总数(size), 元素类型(dtype)
print("Ndim of img1(BGR): {}, img2(Gray): {}".format(img1.ndim, img2.ndim)) # number of rows, columns and channels
print("Shape of img1(BGR): {}, img2(Gray): {}".format(img1.shape, img2.shape)) # number of rows, columns and channels
print("Size of img1(BGR): {}, img2(Gray): {}".format(img1.size, img2.size)) # size = rows * columns * channels
print("Dtype of img1(BGR): {}, img2(Gray): {}".format(img1.dtype, img2.dtype)) # uint8
本例程的运行结果如下:
Ndim of img1(BGR): 3, img2(Gray): 2
Shape of img1(BGR): (512, 512, 3), img2(Gray): (512, 512)
Size of img1(BGR): 786432, img2(Gray): 262144
Dtype of img1(BGR): uint8, img2(Gray): uint8
OpenCV库函数对于数据类型有严格要求,错误的数据类型将导致语法错误。
OpenCV中图像的数据类型的参数命名格式为:
CV_{数字位数}{数字类型}C{通道数}
例如,CV_8UC3表示3通道8位无符号整数格式的矩阵。
OpenCV数据类型与Numpy数据类型的对应关系如表2-1所示。图像处理中最常用的数据类型是8位无符号整数CV_8U,对应于np.uint8。
表2-1 OpenCV与Numpy数据类型的对照关系
数据类型 | OpenCV | Numpy | 取值范围 |
---|---|---|---|
8位无符号整数 | CV_8U | uint8 | 0~255 |
8位符号整数 | CV_8S | int8 | -128~127 |
16位无符号整数 | CV_16U | uint16 | 0~65 535 |
16位符号整数 | CV_16S | int16 | -32 768~32 767 |
32位符号整数 | CV_32S | int32 | -2 147 483 648~-2 147 483 647 |
32位单精度浮点数 | CV_32F | float32 | -FLT_MAX~FLT_MAX, INF, NAN |
64位双精度浮点数 | CV_64F | float64 | -DBL_MAX~DBL_MAX, INF, NAN |
使用ndarray.dtype可以获得Numpy数组的数据类型,使用ndarray.astype可以把数据类型转换成指定的Numpy数据类型。
OpenCV 中的函数 cv.cvtColor() 用于将图像从一个颜色空间转换为另一个颜色空间。可以转换彩色图像的颜色通道顺序、将彩色图像转换为灰度图像,或将图像在RGB空间与其它色彩空间相互转换。
函数原型:
cv.cvtColor(src, code [, dst, dstCn=0]]) → dst
参数说明:
注意问题:
⑴ OpenCV使用RGB模型表示彩色图像时使用BGR格式,按B/G/R顺序存储为多维数组。而PIL、PyQt、Matplotlib等库使用的是R/G/B格式。
⑵ 灰度图像是单通道,在OpenCV、Matplotlib中都是Numpy二维数组。
⑶ 图像中各通道像素值的范围,由图像像素的位深度 depth决定。大多数图像和视频格式为8位无符号整数(CV_8U),取值范围 0~255。
⑷ 图像格式转换通常是线性变换,像素的位深度不影响变换结果;但在进行非线性计算或变换时,需要把输入图像归一化到适当的取值范围,才能得到正确的结果。例如CV_8U由于数据精度较低可能丢失部分信息,使用CV_16U或CV_32F就可以解决这个问题。
⑸ 将图像由GRAY转换为RGB 时,转换规则为:R=G=B=gray。
⑹ 函数cv.cvtColor提供了150多种转换类型,通过以下程序可以查询:
print([i for i in dir(cv) if i.startswith(‘COLOR_’)])
# 图像的颜色空间转换
import cv2 as cv
from matplotlib import pyplot as plt
if __name__ == '__main__':
# 读取原始图像
imgBGR = cv.imread("../images/Lena.tif", flags=1) # 读取为彩色图像
imgRGB = cv.cvtColor(imgBGR, cv.COLOR_BGR2RGB) # BGR 转换为 RGB
imgGRAY = cv.cvtColor(imgBGR, cv.COLOR_BGR2GRAY) # BGR 转灰度图像
imgHSV = cv.cvtColor(imgBGR, cv.COLOR_BGR2HSV) # BGR 转 HSV 图像
imgYCrCb = cv.cvtColor(imgBGR, cv.COLOR_BGR2YCrCb) # BGR 转 YCrCb
imgHLS = cv.cvtColor(imgBGR, cv.COLOR_BGR2HLS) # BGR 转 HLS 图像
imgXYZ = cv.cvtColor(imgBGR, cv.COLOR_BGR2XYZ) # BGR 转 XYZ 图像
imgLAB = cv.cvtColor(imgBGR, cv.COLOR_BGR2LAB) # BGR 转 LAB 图像
imgYUV = cv.cvtColor(imgBGR, cv.COLOR_BGR2YUV) # BGR 转 YUV 图像
# 调用matplotlib显示处理结果
titles = ['BGR', 'RGB', 'GRAY', 'HSV', 'YCrCb', 'HLS', 'XYZ', 'LAB', 'YUV']
images = [imgBGR, imgRGB, imgGRAY, imgHSV, imgYCrCb,
imgHLS, imgXYZ, imgLAB, imgYUV]
plt.figure(figsize=(10, 8))
for i in range(9):
plt.subplot(3, 3, i+1), plt.imshow(images[i], 'gray')
plt.title("{}. {}".format(i+1, titles[i]))
plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()
Qt提供了四个类来处理图像数据:QImage,QPixmap,QBitmap和QPicture。
QImage类提供了独立于硬件的图像表示形式,允许直接访问像素数据,并可用作绘画设备。
QImage类支持Format枚举描述的几种图像格式:单色、8位、32位和alpha混合图像。
QImage提供了多种方式来读取图像文件,在创建QImage对象时可以加载图像文件,也可以在创建对象之后,使用load()或者loadFrameData()函数来加载图像。加载图像时,文件名可以是磁盘上的实际文件,也可以是嵌入到应用程序的资源。
QImage提供了获取图像信息的函数,以及实现图像转换的函数。
函数原型
[QImage](const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr)
用给定的宽度,高度和格式构造一个使用现有内存缓冲区数据的图像。宽度和高度必须以像素指定。bytesPerLine指定每行的字节数。详见官网介绍:QImage Class | Qt GUI 5.10
QImage对象可以按值传递,因为QImage类使用隐式数据共享。QImage对象也可以进行流式处理和比较。
因为QImage是一个QPaintDevice子类,所以可以使用QPainter直接绘制图像。在QImage上使用QPainter时,可以在当前GUI线程之外的其他线程中执行绘制。
from PyQt5 import QtGui
qimage = QtGui.QImage('C:/Users/wxscn/Desktop/test.jpg')
# 直接查询 rect 得不到长宽
rect = qimage.rect()
# 获取长宽的方法 1
w = rect.width()
h = rect.height()
# 获取长宽的方法 2
w_ = qimage.width()
h_ = qimage.height()
print(rect, (w, h), (w_, h_))
# 运行结果:PyQt5.QtCore.QRect(0, 0, 536, 868) (536, 868) (536, 868)
qpixmap = QtGui.QPixmap('C:/Users/wxscn/Desktop/test.jpg')
w = qpixmap.width()
h = qpixmap.height()
print((w, h))
# 运行结果:(536, 868)
qpixmap = qpixmap.scaled(w, h, QtCore.Qt.KeepAspectRatio)
(r,g,b) = QtGui.QColor(qimage.pixel(x, y)).getRgb()[:-1]
QImage中存储的每个像素由一个整数表示。整数的大小因格式而异。QImage支持Format枚举描述的几种图像格式。
使用1位索引将单色图像存储到最多具有两种颜色的颜色表中。有两种不同类型的单色图像:大端(MSB优先)或小端(LSB优先)位顺序。
使用8位索引将8位图像存储到颜色表中,即它们每像素具有一个字节。颜色表是一个QVector<QRgb>,QRgb typedef相当于一个无符号整数,包含格式为0xAARGGBB的ARGB四元组。
32位图像没有颜色表;相反,每个像素都包含一个QRgb值。有三种不同类型的32位图像分别存储RGB(即0xffRRGGBB)、ARGB和预乘ARGB值。在预乘格式中,红色、绿色和蓝色通道乘以alpha分量除以255。
可以使用format()函数检索图像的格式。使用convertToFormat()函数将图像转换为另一种格式。allGray()和isGrayscale()函数用于判断彩色图像是否可以安全地转换为灰度图像。
PyQt提供以下图像格式:
QImage支持许多用于创建新图像的功能:
一些用于就地图像属性的函数:
OpenCV 图像格式不同,转换为 QImage类的方法和参数也不同。
特别地,对于彩色图像,由于 OpenCV 使用 RGB 模型表示彩色图像时使用BGR格式,按B/G/R顺序存储为多维数组,而 PyQt、Matplotlib 等库使用的是 R/G/B 格式。因此,将OpenCV彩色图像转换为QImage时,还要进行颜色通道的顺序调换。
# (1) OpenCV 彩色图像(3通道8位图像)-> QImage
row, col, pix = image.shape[0], image.shape[1], image.strides[0]
qImg = QImage(image.data, col, row, pix, QImage.Format_RGB888).rgbSwapped()
# (2) OpenCV 灰度图像(单通道8位图像)-> QImage
row, col, pix = image.shape[0], image.shape[1], image.strides[0]
qImg = QImage(image.data, col, row, pix, QImage.Format_Indexed8)
# (3) OpenCV 灰度图像(单通道16位图像)-> QImage
row, col, pix = image.shape[0], image.shape[1], image.strides[0]
qimage = QtGui.QImage(image.data, cols, rows, pix, QImage.Format_Grayscale16)
def cvToQImage(image): # OpenCV图像 转换为 PyQt图像
# 8-bits unsigned, NO. OF CHANNELS=1
row, col, pix = image.shape[0], image.shape[1], image.strides[0]
channels = 1 if len(image.shape) == 2 else image.shape[2]
if channels == 3: # CV_8UC3
qImg = QImage(image.data, col, row, pix, QImage.Format_RGB888)
return qImg.rgbSwapped()
elif channels == 1:
qImg = QImage(image.data, col, row, pix, QImage.Format_Indexed8)
return qImg
else:
QtCore.qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
return QImage()
def qPixmapToCV(qtpixmap): # PyQt图像 转换为 OpenCV图像
qImg = qPixmap.toImage() # QPixmap 转换为 QImage
shape = (qImg.height(), qImg.bytesPerLine()*8//qImg.depth())
shape += (4,)
ptr = qImg.bits()
ptr.setsize(qImg.byteCount())
image = np.array(ptr, dtype=np.uint8).reshape(shape) # 定义 OpenCV 图像
image = image[..., :3]
return image
qimage = image.toqimage() # Image 转换为 QImage
qimage = QtGui.QImage(qpixmap) # QPixmap 转换为 QImage
qpixmap = image.toqpixmap() # Image 转换为 QPixmap
qpixmap = QtGui.QPixmap(qimage) # QImage 转换为 QPixmap
例程 GUIdemo4.py 如下:
# OpenCVPyqt04.py
# Demo04 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-01-31
import sys
import cv2 as cv
import numpy as np
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from uiDemo3 import Ui_MainWindow # 导入 uiDemo5.py 中的 Ui_MainWindow 界面类
class MyMainWindow(QMainWindow, Ui_MainWindow): # 继承 QMainWindow 类和 Ui_MainWindow 界面类
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent) # 初始化父类
self.setupUi(self) # 继承 Ui_MainWindow 界面类
self.pushButton_1.clicked.connect(self.click_pushButton_1) # 点击 pushButton_1 触发
self.pushButton_2.clicked.connect(self.click_pushButton_2) # 点击 pushButton_2 触发
self.pushButton_3.clicked.connect(self.close) # 点击 pushButton_3 关闭窗口
return
def click_pushButton_1(self): # 点击 pushButton_1 触发
img = self.openSlot() # 读取图像
print("click_pushButton_1", img.shape)
qImg = self.cvToQImage(img) # OpenCV 转为 PyQt 图像格式
self.label.setPixmap(QPixmap.fromImage(qImg))
# qtPixmap = QPixmap.fromImage(qImg)
# image = self.qPixmapToCV(qtPixmap)
# print("image = self.qPixmapToCV(qtPixmap)", image.shape)
return
def click_pushButton_2(self): # 点击 pushButton_2 触发
img = self.openSlot() # 读取图像
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转为灰度图像
print("click_pushButton_2", img.shape, gray.shape)
qImg = self.cvToQImage(gray) # 灰度图像 转为 PyQt 图像格式
self.label.setPixmap(QPixmap.fromImage(qImg))
return
def openSlot(self, flag=1): # 读取图像文件
# OpenCV 读取图像文件
fileName, _ = QFileDialog.getOpenFileName(self, "Open Image", "../images/", "*.png *.jpg *.tif")
if flag==0 or flag=="gray":
img = cv.imread(fileName, cv.IMREAD_GRAYSCALE) # 读取灰度图像
else:
img = cv.imread(fileName, cv.IMREAD_COLOR) # 读取彩色图像
print(fileName, img.shape)
return img
def saveSlot(self): # 保存图像文件
# 选择存储文件 dialog
saveName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')
if self.img.size == 1:
return
ret = cv.imwrite(saveName, self.img) # OpenCV 写入图像文件
if ret:
print(saveName, self.img.shape)
return
def cvToQImage(self, image): # OpenCV图像 转换为 PyQt图像
# 8-bits unsigned, NO. OF CHANNELS=1
row, col, pix = image.shape[0], image.shape[1], image.strides[0]
channels = 1 if len(image.shape)==2 else image.shape[2]
if channels == 3: # CV_8UC3
qImg = QImage(image.data, col, row, pix, QImage.Format_RGB888)
return qImg.rgbSwapped()
elif channels == 1:
qImg = QImage(image.data, col, row, pix, QImage.Format_Indexed8)
return qImg
else:
QtCore.qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
return QImage()
def qPixmapToCV(self, qPixmap): # PyQt图像 转换为 OpenCV图像
qImg = qPixmap.toImage() # QPixmap 转换为 QImage
shape = (qImg.height(), qImg.bytesPerLine() * 8 // qImg.depth())
shape += (4,)
ptr = qImg.bits()
ptr.setsize(qImg.byteCount())
image = np.array(ptr, dtype=np.uint8).reshape(shape) # 定义 OpenCV 图像
image = image[..., :3]
return image
if __name__ == '__main__':
app = QApplication(sys.argv) # 在 QApplication 方法中使用,创建应用程序对象
myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口
myWin.show() # 在桌面显示控件 myWin
sys.exit(app.exec_()) # 结束进程,退出程序
检查一下应用程序 GUIdemo4 的各项功能:
点击 “3# 按钮”,或工具栏中的 “退出”,关闭图形窗口应用程序;
点击工具栏中的 “帮助”,弹出信息提示框,点击 “OK” 可以关闭信息框;
【本节完】
版权声明:
原创作品,转载必须标注原文链接:https://blog.csdn.net/youcans/article/details/128845326
Copyright 2023 youcans, XUPT
Crated:2023-02-02
文章浏览阅读831次。概率性闪退附带堆栈#00 pc 00000000005975cc /data/app/com.mylafe.game.handsomecat-1/lib/arm64/liblayaair.so (laya::JCServerFileCache::getFileID(char const*)+32) [arm64-v8a]2#01 pc 00000000005d02cc /data/app/com..._liblayaair.so
文章浏览阅读852次。产生背景:之前在win7系统能正常运行,但今天重装系统,把c盘格式化了,在新系统运行mysql就报如下错误:错误提示;E:\mysql5.7\bin>mysqld --console2018-04-15T14:57:12.199569Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --..._shutting down plugin 'csv
文章浏览阅读702次。本发明涉及扩频通信领域。背景技术:在抗干扰性和保密性要求较高的卫星通信以及军事通信领域,跳码直扩通信系统得到了较广泛的应用,而跳码同步捕获是跳码直扩通信系统中的关键技术。所谓捕获实际上就是在一个周期内通过相关运算进行伪码相位的搜索过程。在扩频接收系统中伪码捕获算法的种类有很多,如发送参考序列、统一定时法,虽然这些方法的设计结构简单易于实现,但是局限性较大,性能较差,捕获时间较长,耗时太大无法完成同..._扩频捕获
文章浏览阅读2.4k次。array_pad() 用值将数组填补到指定长度【功能】 该函数将返回指定数组的一个复制,并用指定的值将其填补到指定的长度。 如果指定长度为正,则数组被填补到右侧,如果为负则从左侧开始填补。 如果指定长度的绝对值小于或等于原数组的长度,则没有任何填补 有可能一次最多填补1048576个数组元_array补充到一定长度
文章浏览阅读162次。本文是作者本人学习过程中的笔记总结,如若文中有不正确,或需要补充的地方,欢迎在评论区中留言。1. width_盒子模型实际宽度用加margin吗
文章浏览阅读337次。初学者的回归分析 - 第二部分使用基于树的算法(决策树、随机森林、XGboost)建立一个ML回归模型简介第2.1部分 建立机器学习管道∘ 第1步:收集数据∘ 第二步:将数据可视化(问自己这些问题并回答)∘ 第三步:清理数据∘ 第四步:訓練模型∘ 第五步:評估∘ 第六步:使用hyperopt进行超参数调整∘ 第七步:选择最佳模型和预测结果第2.2部分:分析ML算法∘ 什么是决策树?∘ 什么是随机森林?∘ 什么是极限梯度提升法?(XGBoost)∘ 决策树 vs 随机森林 v_seaborn r2_score round(2)
文章浏览阅读217次。6月3日,教育部大学物理教学指导委员会主任委员、清华大学教授王青到广西大学,对物理学院专业建设进行指导。上午,物理学院负责人以及专业负责人分别作了关于学院一流专业建设思路、物理电子创陪班教学计划、物理专业培养计划和天文精英班办学特色、电子科学与技术专业培养计划和卓越工程师培养思路的报告,王青教授听取报告后,肯定了物理学院在专业建设方面做出的成绩,并给出有建设性的意见和建议。随后,王青教授作题为“量..._清华大学数字化研究所王青
文章浏览阅读290次。<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %><!DOCTYPE html&._layui-v1.0.9_rls 调用laypage
文章浏览阅读3k次。首先排除代码之间的换行符<br>,出现这个问题是因为图片为内联元素,有文字特性,默认存在间隙,所以会出现空白部分。可以将图片设置为block元素。img{ display:block;}还有种方法就是给父级设置fontsize:0,logo先不设置block转自:https://www.fengjinwei.com/blog-847722.html..._上下图片之间的间距不同
文章浏览阅读257次。1. Broker 配置信息 属性 默认值 描述 broker.id 必填参数,broker的唯一标识 log.dirs /tmp/kafka-logs Kafka数据存放的目录。可以指定多个目录,中间用逗号分隔,..._kafka configuration property queue.buffering.max.kbytes is a producer proper
文章浏览阅读1.6w次,点赞6次,收藏33次。wangeditor: 上传图片+上传视频+上传附件(自定义)完整使用一:项目需求:①角色为管理员可以新增编辑文章 + ②点击可以看文章详情 +③ 角色为管理员可以修改编辑文章二:效果:①角色为管理员可以新增编辑文章步骤:①下载安装相关依赖 npm i wangeditor --save②引入③初始化创建编辑器代码中的initialEditor函数④自定义上传附件按钮主要思路:在编辑器上增加新的菜单按钮 --》实例化按钮 --》结合an..._wangeditor上传图片
文章浏览阅读1.3w次。源端口是大于的随即端口,目的端口是23。telnet的默认端口号是多少查看端口在windows/xp/server中要查看端口,可以使用netstat命令:依次点击“开始→运行”,键入“cmd”并回车,打开命令提示符窗口。在命令提示符状态下键入“netstat-a-n”,按下回车键后就可以看到以数字形式显示的tcp和udp连接的端口号及状态。小知识:netstat命令用法命令格式:netstat-..._telnet源端口和目的端口