OpenCV-Python实战(1)——OpenCV简介与图像处理基础(1)

  1. 显示结果,将图像处理完成后的结果以人类可读的方式进行呈现(例如,在图像中绘制边界框,有时也可能需要将其保存到磁盘)。

此外,上述第2步图像处理可以进一步分为三个不同的处理级别:

  1. 低层处理(或者在不引起歧义的情况下可以称为预处理),通常将一个图像作为输入,然后输出另一个图像。可在此步骤中应用的步骤包括但不限于以下方法:噪声消除、图像锐化、光照归一化以及透视校正等;

  2. 中层处理:是将预处理后的图像提取其主要特征(例如采用 DNN 模型得到的图像特征),输出某种形式的图像表示,它提取了用于图像进一步处理的主要特征。

  3. 高层处理:接受中层处理得到的图像特征并输出最终结果。例如,处理的输出可以是检测到的人脸.

像素、颜色、通道、图像和颜色空间

在表示图像时,有多种不同的颜色模型,但最常见的是红、绿、蓝 (RGB) 模型。

RGB 模型是一种加法颜色模型,其中原色 (在RGB模型中,原色是红色 R、绿色 G 和蓝色 B) 混合在一起就可以用来表示广泛的颜色范围。

每个原色 (R, G, B) 通常表示一个通道,其取值范围为[0, 255]内的整数值。因此,每个通道有共256个可能的离散值,其对应于用于表示颜色通道值的总比特数 ( 2 8 = 256 2^8=256 28=256)。此外,由于有三个不同的通道,使用 RGB 模型表示的图像称为24位色深图像:

加色法

在上图中,可以看到 RGB 颜色空间的“加法颜色”属性:

  1. 红色加绿色会得到黄色

  2. 蓝色加红色会得到品红

  3. 蓝色加绿色会得到青色

  4. 三种原色加在一起得到白色

因此,如前所述,RGB 颜色模型中,特定颜色可以由红、绿和蓝值分量合成表示,将像素值表示为 RGB 三元组 (r, g, b)。典型的 RGB 颜色选择器如下图所示:

颜色选择器

分辨率为 800×1200 的图像是一个包含800列和1200行的网格,每个网格就是称为一个像素,因此其中包含 800×1200=96 万像素。应当注意,图像中有多少像素并不表示其物理尺寸(一个像素不等于一毫米)。相反,像素的大小取决于为该图像设置的每英寸像素数 (Pixels Per Inch, PPI)。图像的 PPI 一般设置在 [200-400] 范围内。

计算PPI的基本公式如下:

  1. PPI=宽度(像素) / 图像宽度(英寸)

  2. PPI=高度(像素) / 图像高度(英寸)

例如,一个4×6英寸图像,图像分辨率为 800×1200,则PPI是200。

图像描述

图像可以描述为2D函数 f ( x , y ) f(x, y) f(x,y),其中 ( x , y ) (x, y) (x,y) 是空间坐标,而 f ( x , y ) f(x, y) f(x,y) 是图像在点 ( x , y ) (x, y) (x,y) 处的亮度或灰度或颜色值。另外,当f(x, y)和(x, y)值都是有限离散量时,该图像也被称为数字图像,此时:

  1. x ∈ [ 0 , h − 1 ] x∈ [0, h-1] x∈[0,h−1],其中 h h h 是图像的高度

  2. y ∈ [ 0 , w − 1 ] y∈ [0, w-1] y∈[0,w−1],其中 w w w 是图像的宽度

  3. f ( x , y ) ∈ [ 0 , L − 1 ] f(x, y)∈ [0,L-1] f(x,y)∈[0,L−1],其中 L = 256 L=256 L=256 (对于8位灰度图像)

彩色图像也可以用同样的方式表示,只是我们需要定义三个函数来分别表示红色、绿色和蓝色值。这三个单独的函数中的每一个都遵循与为灰度图像定义的 f ( x , y ) f(x, y) f(x,y) 函数相同的公式。我们将这三个函数的子索引 R、G 和 B 分别表示为 f R ( x , y ) f_R(x, y) fR​(x,y)、 f G ( x , y ) f_G(x, y) fG​(x,y) 和 f B ( x , y ) f_B(x, y) fB​(x,y)。

同样,黑白图像也可以表示为相同的形式,其仅需要一个函数来表示图像,且 f ( x , y ) f(x, y) f(x,y) 只能取两个值。通常,0 表示黑色、1 表示白色。

下图显示了三种不同类型的图像(彩色图像、灰度图像和黑白图像):

不同类型图像示例

数字图像可以看作是真实场景的近似,因为 f ( x , y ) f(x, y) f(x,y) 值是有限的离散量。此外,灰度和黑白图像每个点只对应有一个值,彩色图像每个点需要三个函数对应于图像的红色、绿色和蓝色分量。

图像文件类型

尽管在 OpenCV 中处理的图像时,可以将图像看作 RGB 三元组的矩阵(在 RGB 图像模型情况下),但它们不一定是以这种格式创建、存储或传输的。有许多不同的文件格式,如GIF、PNG、位图或JPEG,使用不同形式的压缩(无损或有损)来更有效地表示图像。

下表列示了 OpenCV 支持的文件格式及其关联的文件扩展名:

| 文件格式 | 文件扩展名 |

| — | — |

| Windows bitmaps | *.bmp和*.dib |

| JPEG files | *.JPEG、*.jpg 和 *.jpe |

| JPEG 2000 files | *.jp2 |

| Portable Network Graphics | *.png |

| Portable image format | *.pbm、*.pgm 和 *.ppm |

| TIFF files | *.TIFF 和 *.tif |

对图像应用无损或有损压缩算法,可以得到比未压缩图像占据存储空间小的图像。其中,在无损压缩算法中,得到的图像与原始图像等价,也就是说,经过反压缩过程后,得到的图像与原始图像完全等价(相同);而在有损压缩算法中,得到的图像并不等同于原始图像,这意味着图像中的某些细节会丢失,在许多有损压缩算法中,压缩级别是可以调整的。

OpenCV中的坐标系

为了更好的展示 OpenCV 中的坐标系以及如何访问各个像素,查看以下低分辨率图像为例:

低分辨率图片

这个图片的尺寸是 32×41 像素,也就是说,这个图像有 1312 个像素。为了进一步说明,我们可以在每个轴上添加像素计数,如下图所示:

添加像素计数

现在,我们来看看 ( x , y ) (x, y) (x,y) 形式的像素索引。请注意,像素索引起始值为零,这意味着左上角位于 ( 0 , 0 ) (0, 0) (0,0),而不是 ( 1 , 1 ) (1, 1) (1,1)。下面的图像,索引了 4 个单独的像素,图像的左上角是原点的坐标:

像素索引

单个像素的信息可以从图像中提取,方法与 Python 中引用数组的单个元素相同。

OpenCV中的通道顺序

在 OpenCV 使用中,使用的颜色通道顺序为 BGR 颜色格式而不是 RGB 格式。可以在下图中看到三个通道的顺序:

OpenCV通道顺序

BGR 图像的像素结构如下图所示,作为演示,图示详细说明了如何访问pixel(y=n, x=1):

像素访问

Tips:OpenCV 的最初开发人员选择了 BGR 颜色格式(而不是 RGB 格式),是因为当时 BGR 颜色格式在软件供应商和相机制造商中非常流行,因此选择 BGR 是出于历史原因。

此外,也有其他 Python 包使用的是 RGB 颜色格式(例如,Matplotlib 使用 RGB 颜色格式,Matplotlib 是最流行的 2D Python 绘图库,提供多种绘图方法,可以查看 Python-Matplotlib 可视化获取更多详细信息)。因此,我们需要知道如何将图像从一种格式转换为另一种格式。

当我们掌握了将图像从一种格式转换为另一种格式的方法后,就可以选择使用 OpenCV 进行图像处理,同时利用 Matplotlib 包提供的函数来显示图像,接下来,让我们看看如何处理两个库采用的不同颜色格式。

首先,我们使用 cv2.imread() 函数加载图像:

import cv2

img_OpenCV = cv2.imread(‘sigonghuiye.jpeg’)

图像存储在 img_OpenCV 变量中,因为 cv2.imread() 函数以 BGR 顺序加载图像。然后,我们使用 cv2.split() 函数将加载的图像分成三个通道 (b, g, r) 。这个函数的参数就是我们要分割的图像:

b, g, r = cv2.split(img_OpenCV)

下一步是合并通道(以便根据通道提供的信息构建新图像),但合并时顺序与原图像不同。我们更改 b 和 r 通道的顺序以遵循 RGB 格式,即我们所需要的 Matplotlib 格式:

img_matplotlib = cv2.merge([r, g, b])

此时,我们有两个图像 (img_OpenCV 和 img_matplotlib),接下来,我们将分别使用 OpenCV 和 Matplotlib 绘制她们,以便我们可以对比结果。首先,我们将用 Matplotlib 显示这两个图像。

为了在同一个窗口中使用 Matplotlib 显示两个图像,我们将使用 subplot,它将多个图像放置在同一个窗口中。可以在 subplot 中使用三个参数,例如 subplot(m,n,p),此时,子图处理 m × n m \times n m×n 网格中的图,其中 m m m 确定行数, n n n 确定列数,而 p p p 确定要在网格中放置图的位置。要使用 Matplotlib 显示图像,需要使用 imshow 函数。

在这种情况下,当我们水平显示两个图像时, m = 1 m = 1 m=1、 n = 2 n = 2 n=2 。我们将对第一个子图 (img_OpenCV) 使用 p = 1 p = 1 p=1,对第二个子图(img_matplotlib) 使用 p = 2 p = 2 p=2:

from matplotlib import pyplot as plt

plt.subplot(121)

plt.imshow(img_OpenCV)

plt.subplot(122)

plt.imshow(img_matplotlib)

plt.show()

程序输出如下图所示:

matplotlib绘制结果

可以看出,第一个子图以错误的颜色( BGR 顺序)显示图像,而第二个子图以正确的颜色( RGB 顺序)显示图像。接下来,我们使用 cv2.imshow() 显示两个图像:

cv2.imshow(‘bgr image’, img_OpenCV)

cv2.imshow(‘rgb image’, img_matplotlib)

cv2.waitKey(0)

cv2.destroyAllWindows()

以下屏幕截图显示了执行上述代码获得的结果:

OpenCV绘制结果

正如预期的那样,屏幕截图中,第一张图以正确的色彩显示图像,而第二张图以错误的颜色显示图像。

此外,如果我们想在同一个窗口中显示两个图像,可以构建一个包含这两个图像的拼接图像,将两张图片水平连接起来。为此,我们需要使用 NumPyconcatenate() 方法。该方法的参数是要连接的两个图像和要在哪个轴上进行堆叠,这里,我们令 axis = 1 (水平堆叠它们):

import numpy as np

img_concats = np.concatenate((img_OpenCV, img_matplotlib), axis=1)

cv2.imshow(‘bgr image and rgb image’, img_concats)

cv2.waitKey(0)

cv2.destroyAllWindows()

下图显示了连接后的图像:

拼接后显示

需要考虑的一个因素是 cv2.split() 是一项耗时的操作。如果确实需要划分不同通道,应当首先考虑使用 NumPy 索引。例如,如果想获取图像的一个通道,则可以使用 NumPy 索引获取通道:

B = img_OpenCV[:, :, 0]

G = img_OpenCV[:, :, 1]

R = img_OpenCV[:, :, 2]

另一个需要注意的是,可以使用 NumPy 在一条语句中将图像从 BGR 转换为 RGB:

img_matplotlib = img_OpenCV[:, :, ::-1]

在不同颜色空间中访问和操作OpenCV中的像素

本节将介绍如何使用 OpenCV 访问和读取像素值以及如何修改它们。此外,还将学习如何访问图像属性。如果想一次处理多个像素,则需要创建图像区域 (Region of Image, ROI)。

在 Python 中,图像表示为 NumPy 数组。因此,示例中包含的大多数操作都与 NumPy 相关,建议需要对 NumPy 包一些了解,才能更好明白示例代码的原理,但即使不了解也没关系,必要时会对所用函数进行讲解。

彩色图像访问和操作OpenCV中的像素

现在,我们来看看如何在 OpenCV 中处理BGR图像。如上所述,OpenCV 加载彩色图像时,蓝色通道是第一个,绿色通道是第二个,红色通道是第三个。

首先,使用 cv2.imread() 函数读取图像。图像应该在工作目录中,或者应该提供图片的完整路径。在本例中,读取 sigonghuiye.jpeg 图像并将其存储在img变量中:

img = cv2.imread(‘sigonghuiye.jpeg’)

图像加载到 img 后,可以获得图像的一些属性。我们要从加载的图像中提取的第一个属性是 shape,它将告诉我们行、列和通道的数量(如果图像是彩色的)。我们将此信息存储在 dimensions 变量中:

dimensions = img.shape

第二个属性是图像的大小(img.size=图像高度 × 图像宽度 × 图像通道数):

total_number_of_elements= img.size

第三个属性是图像数据类型,可以通过 img.dtype 获得。因为像素值在 [0-255] 范围内,所以图像数据类型是 uint8 (unsigned char):

image_dtype = img.dtype

上面示例中,我们已经使用了 cv2.imshow() 函数来在窗口中显示图像,这里我们对其进行更详细的介绍,使用 cv2.imshow() 函数显示图像时,窗口会自动适应图像大小。此函数的第一个参数是窗口名,第二个参数是要显示的图像。在这种情况下,由于加载的图像已存储在 img 变量中,因此使用此变量作为第二个参数:

cv2.imshow(“original image”, img)

显示图像后,我们来介绍下键盘绑定函数——cv2.waitKey(),它为任何键盘事件等待指定的毫秒数。参数是以毫秒为单位的时间。当执行到此函数时,程序将暂停执行,当按下任何键后,程序将继续执行。如果毫秒数为 0 (cv2.waitKey(0)),它将无限期地等待键盘敲击事件:

cv2.waitKey(0)

要访问(读取)某个像素值,我们需要向 img 变量(包含加载的图像)提供所需像素的行和列,例如,要获得 ( x = 40 , y = 6 ) (x=40, y=6) (x=40,y=6) 处的像素值 :

(b, g, r) = img[6, 40]

我们在三个变量 (b, g, r) 中存储了三个像素值。请牢记 OpenCV 对彩色图像使用 BGR 格式。另外,我们可以一次仅访问一个通道。在本例中,我们将使用所需通道的行、列和索引进行索引。例如,要仅获取像素 ( x = 40 , y = 6 ) (x=40, y=6) (x=40,y=6) 处的蓝色值:

b = img[6, 40, 0]

像素值也可以以相同的方式进行修改。例如,要将像素 (x=40, y=6) 处设置为红色:

img[6, 40] = (0, 0, 255)

有时,需要处理某个区域而不是一个像素。在这种情况下,应该提供值的范围(也称切片),而不是单个值。例如,要获取图像的左上角:

top_left_corner = img[0:50, 0:50]

变量 top_left_corner 可以看做是另一个图像(比img小),但是我们可以用同样的方法处理它。

最后,如果想要关闭并释放所有窗口,需要使用 cv2.destroyAllWindows() 函数:

cv2.destroyAllWindows()

灰度图像访问和操作OpenCV中的像素

灰度图像只有一个通道。因此,在处理这些图像时会引入一些差异。我们将在这里重点介绍这些差异,相同的部分不再赘述。

同样,我们将使用 cv2.imread() 函数来读取图像。在这种情况下,需要第二个参数,因为我们希望以灰度加载图像。第二个参数是一个标志位,指定读取图像的方式。以灰度加载图像所需的值是 cv2.IMREAD_grayscale:

gray_img = cv2.imread(‘logo.png’, cv2.IMREAD_GRAYSCALE)

在这种情况下,我们将图像存储在gray_img变量中。如果我们打印图像的尺寸(使用 gray_img.shape ),只能得到两个值,即行和列。在灰度图像中,不提供通道信息:

dimensions = gray_img.shape

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-kfmeqKaM-1713601147159)]

[外链图片转存中…(img-KJRbKEzE-1713601147160)]

[外链图片转存中…(img-hdMTWbxL-1713601147161)]

[外链图片转存中…(img-e5ApdmIm-1713601147161)]

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以扫码获取!!!(备注:Python)

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值