OpenCV入门

  • Post author:
  • Post category:其他




一、图像处理基本操作



1.读取图像

使用的方法:

imread()


cv.imread(filename[,flags])

参数说明:

📊filename:要读取文件的名称

📊flages:

  • cv.IMREAD_COLOR: 加载彩色图像。任何图像的透明度都会被忽视。它是默认标志。
  • cv.IMREAD_GRAYSCALE:以灰度模式加载图像
  • cv.IMREAD_UNCHANGED:加载图像,包括alpha通道


注意

除了这三个标志,你可以分别简单地传递整数1、0或-1。

import cv2 as cv

image_1 = cv.imread('1.png', 1)
image_2 = cv.imread("D:\\learn\\1.png", 1)

3、4行代码效果相同,只不过第四行代码可以读取别的路径下的图片。



2.显示图像

使用的方法:

imshow()



waitkey()



destroyAllWindows()

  1. imshow():显示图像


    cv.imshow(winname,mat)

    参数说明:

    📊 winname:显示图像窗口的名称

    📊 mat:要显示的图像

  2. waitKey():等待用户按下键盘按键的时间。当用户按下键盘的任意按键时,将执行waitKey() 方法,并且获取此方法的返回值。


    retval = cv.waitKey(delay)

    参数说明:

    📊 retval:与被按下键相对应的ASCII码。例如ESC键的ACSII码为27,当用户按下ESC键时,waitKey()方法返回值时27,如果不是就返回-1。

    📊 delay:等待用户按下键盘上按键的时间。单位为ms,如果值为负数,0或者为空时表示无限等待。

  3. destroyAllWindows():销毁正在显示图像的窗口


    cv.destroyAllWindows()


实例:

import cv2 as cv

image = cv.imread('1.png', 1)

cv.imshow("taylor", image)
retval = cv.waitKey()
cv.destroyAllWindows()

print(retval)

运行结果:按下ESC

27

在这里插入图片描述


注意:

1️⃣窗口的名称不能为中文,否则会出现乱码。

2️⃣为了能够正常显示图像,

cv.waitKey()

不能省略,否则图像会一闪而过。


补充:

1️⃣

cv.destroyWindow()

:销毁指定名称的窗口。

import cv2 as cv

image = cv.imread('1.png', 1)

cv.imshow("taylor", image)
retval = cv.waitKey()
cv.destroyWindow("taylor")

print(retval)

2️⃣在特殊情况下,用户可以先创建一个空窗口,然后再将图像传入窗口。这种情况下可以指定窗口的大小。这是通过

cv.nameWindow()

完成的。默认情况下,该标志为

cv.WINDOW_AUTOSIZE

,但是如果将标志指定为

cv.WINDOW_NORMAL

就可以调整窗口的大小。


实例

import cv2 as cv

image = cv.imread("1.png")
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.imshow("taylor", image)
cv.waitKey()
cv.destroyAllWindows()

运行结果:窗口是可以用鼠标调整大小的

在这里插入图片描述


注意

代码的第3,4行要显示的窗口名称必须保持一致,否则会出现两个窗口。



3.保存图像

使用方法:

imwrite()

:可用于按照指定路径保存图像。


cv.imwrite(filename,img)

参数说明:

📊filename:保存图像的完整路径。

📊img:要保存的图像。


实例

import cv2 as cv

image = cv.imread("1.png", 0)
cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.imshow("taylor", image)
cv.imwrite("D:\\learn\\2.png", image)
cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述



4.图像属性

使用方法:shape、size、dtype

1️⃣shape:如果图像是彩色图像,那么获取的是一个由图像的像素列数,行数和通道数组成的数组;

​ 如果图像是灰度图像,那么获取的是一个由图像的像素列数,行数组成的数组。

2️⃣size:获取的是一个包含图像的像素个数,值大小等于像素列数×像素行数×通道数(如果是灰度图像,通道数就是1)。

3️⃣dtype:获取的是图像的数据类型。

import cv2 as cv

image = cv.imread("1.png")
print("图像的色彩属性:")
print("shape = ", image.shape)
print("size = ", image.size)
print("dtype = ", image.dtype)

运行结果:

图像的色彩属性:
shape =  (1080, 1920, 3)
size =  6220800
dtype =  uint8



二、图像数字化基础

图像数字化指的是用数字表示图像。计算机通常会把像素值处理为处理为256个灰度级别,这256个灰度级别分别用区间[0,255]中的数值表示。其中0表示纯黑色,255表示纯白色。



1.像素

像素是构成数字图像的基本单位。

  1. 确定像素的位置

    首先要确定此图像水平方向和垂直方向上的像素个数。

在这里插入图片描述

在OpenCV中正确表示某个像素坐标的表示方法是(y,x)。


实例

import cv2 as cv

image = cv.imread("1.png")
px = image[1079, 1919]
  1. 获取像素的BGR值

    上一小节已经获取了坐标为[1079,1919]上的像素px,那么使用

    print()

    方法打印这个像素得到的就是这个像素BGR值。

    import cv2 as cv
    
    image = cv.imread("1.png")
    px = image[1079, 1919]
    print("这个像素的BGR值为", px)
    

    运行 结果为:

    这个像素的BGR值为 [179 177 177]
    

    在了解这三组数字代表的含义之前,先了解一下何为三基色。

    人眼能感受到红色、绿色和蓝色三种颜色,把这三种颜色称为三基色,同时将三基色以不同比例混合就可以得到不同的颜色。

    那么在计算机中要对颜色进行编码,就要使用到色彩空间。

    其中较为常用的就是RGB色彩空间。以此为例,在RGB色彩空间中,有三个颜色通道,分别是:红色R、绿色G和蓝色B。并且每个颜色都在[0,255]之间取值。

    不过需要注意的是,OpenCV在输出通道顺序的时候是相反的,是按照BGR输出。

    在OpenCV中获取像素的BGR值方法有两种:

    import cv2 as cv
    
    image = cv.imread("1.png")
    px = image[1079, 1919]
    print("这个像素的BGR值为", px)
    
    import cv2 as cv
    
    image = cv.imread("1.png")
    print("这个像素的B值为", image[1079, 1919, 0])
    print("这个像素的G值为", image[1079, 1919, 1])
    print("这个像素的R值为", image[1079, 1919, 2])
    

    运行结果:

    参考前文程序输出。

  2. 修改像素的BGR值

    前一节获得了坐标为[1079,1919]的像素的BGR值,如果要修改BGR值,代码为:

    import cv2 as cv
    
    image = cv.imread("1.png")
    px = image[1079, 1919]
    print("这个像素修改前的BGR值为", px)
    px = [255, 255, 255]
    print("这个像素修改后的BGR值为", px)
    

    运行结果:

    这个像素修改前的BGR值为 [179 177 177]
    这个像素修改后的BGR值为 [255, 255, 255]
    


    注意:

    如果将每个像素的R、G、B值改为相等大小时,就可以得到灰度图像。全为0时就是纯黑色,全为255时就是纯白色。

    如果要修改指定区域中的所有像素就可以使用2层for循环嵌套:

    import cv2 as cv
    
    image = cv.imread("1.png", 1)
    cv.namedWindow("taylor", cv.WINDOW_NORMAL)
    
    for i in range(0, 79):
        for j in range(0, 19):
            image[i, j] = [0, 0, 0]
    
    cv.imshow("taylor", image)
    cv.waitKey()
    cv.destroyAllWindows()
    
    

    运行结果:

    在这里插入图片描述


⚠️


警告

文章刚开头介绍了

imread()

方法,前一节介绍了如何修改指定区域的元素。

比较下面图片中代码的区别:

在这里插入图片描述

如果图片是以彩色图像 加载的时候,那么修改指定区域像素的颜色,可以直接使用列表[0,0,0]

如果图像不是以彩色图像加载的时候,那么修改指定区域像素的颜色,不能直接使用列表[0,0,0],否则会报错:

ValueError: setting an array element with a sequence.

必须要使用数组。

Numpy是用于快速数组计算的优化库。因此,简单地访问每个像素值并对其进行修改将非常缓慢,因此不建议使用。


注意

上面的方法通常用于选择数组的区域,例如前5行和后3列。对于单个像素访问,Numpy数组方法array.item()和array.itemset())被认为更好,但是它们始终返回标量。如果要访问所有B,G,R值,则需要分别调用所有的array.item()。

更好的像素访问和编辑方法:

# 访问 RED 值
>>> img.item(10,10,2)
59
# 修改 RED 值
>>> img.itemset((10,10,2),100)
>>> img.item(10,10,2)
100



2.色彩空间

除了前文介绍的RGB和BGR色彩空间,还有两个比较常见的色彩空间:GRAY和HSV色彩空间。


1.GRAY色彩空间

GRAY色彩空间通常指的是灰度图像,每个像素都是从黑到白([0,255]).0表示纯黑色,255表示纯白色。


2.从RGB/BGR色彩空间到GRAY色彩空间

使用方法:

dst = cv.cvtColor(src,code)

参数说明:

📊dst:转换后的图像;

📊src:转换前的初始图像

📊code:色彩空间转换码

色彩空间转换码 含义
cv.COLOR_BGR2GRAY 从BGR色彩空间转换到GRAY色彩空间
cv.COLOR_RGB2GRAY 从RGB色彩空间转换到GRAY色彩空间


实例

import cv2 as cv

image = cv.imread("1.png", 1)
image_GRAY = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

cv.imshow("taylor", image_GRAY)
cv.waitKey()
cv.destroyAllWindows()

1️⃣色彩空间转换码还有很多,读者请自行查阅。

2️⃣虽然色彩空间转换是双向的,但是灰度图像是无法转换成彩色图像的。因为彩色图像在转换成灰度图像的时候丢失了颜色比例,并且无法找回。


3.HSV色彩空间

HSV色彩空间是基于色调、饱和度、亮度而言的。

色调 H 指的是光的颜色。在OpenCV中,色调在[0,180]内取值。红、黄、绿和蓝分别是:0、30、60、120。

饱和度 S 指的是色彩的深浅。在OpenCV中,饱和度在[0,255]内取值。饱和度为0时,图像将变为灰度图像。

亮度 V 指的是光的明暗。在OpenCV中,亮度在[0,255]内取值。


实例

import cv2 as cv

image = cv.imread("1.png", 1)
image_GRAY = cv.cvtColor(image, cv.COLOR_BGR2HSV)

cv.imshow("taylor", image_GRAY)
cv.waitKey()
cv.destroyAllWindows()

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qWW2H1Si-1649246389207)(D:\learn\opencv入门.assets\image-20220406101443885.png)]



3.通道


1.拆分通道

使用方法:

split()


b,g,r = cv.split(bgr_image)

参数说明:

📊b:B通道图像

📊g:G通道图像

📊R:R通道图像

📊bgr_image:一幅RGB图像

拆分一幅BGR图像通道的顺序是B→G→R,因此等号左边必须是“b, g, r”。


实例:

import cv2 as cv

image = cv.imread("1.png", 1)
b, g, r = cv.split(image)

cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_B", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_G", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_R", cv.WINDOW_NORMAL)

cv.imshow("taylor", image)
cv.imshow("taylor_B", b)
cv.imshow("taylor_G", g)
cv.imshow("taylor_R", r)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述

有的读者会发现得到的B、G、R通道图像是3幅不同灰度的图像。这是因为在

cv.imshow("taylor_B",b)

时候,BGR这三通道的值都为B通道的值。其余两个同理。所以只要 B=G=R 就可以得到灰度图像。


实例:

import cv2 as cv

image = cv.imread("1.png", 1)
b, g, r = cv.split(image)


cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_B", cv.WINDOW_NORMAL)


for i in range(0, 1079):
    for j in range(0, 1919):
        a = image[i, j]
        image[i, j] = a[0]

cv.imshow("taylor", image)
cv.imshow("taylor_B", b)


cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述

拆分一幅图像的HSV通道。只需先将图像先转换为HSV图像即可,后面步骤一致。


实例:

import cv2 as cv

image = cv.imread("1.png", 1)
image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)
h, s, v = cv.split(image_HSV)

cv.namedWindow("taylor", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_H", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_S", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_V", cv.WINDOW_NORMAL)

cv.imshow("taylor", image_HSV)
cv.imshow("taylor_H", h)
cv.imshow("taylor_S", s)
cv.imshow("taylor_V", v)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7fPJp6E-1649246389208)(D:\learn\opencv入门.assets\image-20220406183300394.png)]


2.合并通道

合并通道是拆分通道的逆过程。

使用方法:

bgr = cv.merge([b,g,r])

参数说明:

📊bgr:按照B→G→R的顺序合并后得到的图像。

📊b:B通道图像

📊g:G通道图像

📊r:R通道图像

如果用户要按照R→G→B顺序合并,调换顺序即可。


实例:

import cv2 as cv

image = cv.imread("1.png", 1)
b, g, r = cv.split(image)
cv.namedWindow("taylor_original", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_merge_BGR", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_merge_RGB", cv.WINDOW_NORMAL)

cv.imshow("taylor_original", image)
image_merge_BGR = cv.merge([b, g, r])
image_merge_RGB = cv.merge([r, g, b])
cv.imshow("taylor_merge_BGR", image_merge_BGR)
cv.imshow("taylor_merge_RGB", image_merge_RGB)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述

相信读者已经发现,按照不同的合并顺序得到的图像也是不一样的。

HSV通道合并同理。不多赘述。


3.合理运用拆分通道和合并通道


在HSV色彩空间内,如果保持其中两个通道的值不变,调整第三个通道,会得到相应的艺术效果。

**实例:**只把H通道的值改为180

import cv2 as cv

cv.namedWindow("taylor_H180", cv.WINDOW_NORMAL)
image = cv.imread("1.png", 1)

image_HSV = cv.cvtColor(image, cv.COLOR_BGR2HSV)

h, s, v = cv.split(image_HSV)

h[:] = 180

image_HSV_merge = cv.merge([h, s, v])

image_HSV2BGR = cv.cvtColor(image_HSV_merge, cv.COLOR_HSV2BGR)

cv.imshow("taylor_H180", image_HSV2BGR)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述

⁉️


疑问


前一段程序中的第十行做出以下变动:

在这里插入图片描述

代码变换前后,输出图片的效果是一样的???

使用

type()

查看 h 数据类型的时候发现其类型是

<class 'numpy.ndarray'>

,不是list。


4.alpha通道

前文说明了BGR色彩空间包含了3个通道,在此基础上再加上一个A通道就构成了alpha色彩空间(BRGA色彩空间)。A用于设置图像的透明度。A在区间[0,255]内取值。0表示纯透明。


实例:

import cv2 as cv

cv.namedWindow("taylor_BGRA_A200", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_BGRA", cv.WINDOW_NORMAL)

image = cv.imread("1.png", 1)

image_BRGA = cv.cvtColor(image, cv.COLOR_BGR2BGRA)

b, g, r, a = cv.split(image_BRGA)
a[:] = 200

image_BRGA_A200 = cv.merge([b, g, r, a])

cv.imshow("taylor_BGRA_A200", image_BRGA_A200)
cv.imshow("taylor_BGRA", image_BRGA)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述

虽然从输出的图片看不出区别,但是将这两幅图使用

imwrite()

就可以看出区别:


实例:

import cv2 as cv

cv.namedWindow("taylor_BGRA_A200", cv.WINDOW_NORMAL)
cv.namedWindow("taylor_BGRA", cv.WINDOW_NORMAL)

image = cv.imread("1.png", 1)

image_BGRA = cv.cvtColor(image, cv.COLOR_BGR2BGRA)

b, g, r, a = cv.split(image_BGRA)
a[:] = 200

image_BGRA_A200 = cv.merge([b, g, r, a])

cv.imshow("taylor_BGRA_A200", image_BGRA_A200)
cv.imshow("taylor_BGRA", image_BGRA)

cv.imwrite("D:/learn/taylor_BGRA_A200.png", image_BGRA_A200)
cv.imwrite("D:/learn/taylor_BGRA.png", image_BGRA)

cv.waitKey()
cv.destroyAllWindows()

运行结果:

在这里插入图片描述



4.使用NumPy操作像素



4.1 概述

​ array属性:

  • ​ shape:返回一个数组,表示array维度
  • ​ ndim:一个数字,返回array的维度数目
  • ​ size:一个数字,返回array的元素个数
  • ​ dtype:一个数组,返回array的数据类型



4.2 数组类型

名称 类型
bool_ 布尔数据类型
int_ 默认的整数类型,类似C语言中long,int32、int64
intc 和C语言中的int类型一样,一般是int32或者int64
intp 用于索引的整数类型,类似于C中色ssize_t,一般还是int32或int64
int8 一字节相同的8位整数,-128~127
int16 两字节(16位),-32768~32767
int32、int64 不多赘述
uint8 无符号整数
uint16.unit32、uint64 不多赘述
float_ float64的简写
float16 半精度浮点数,包括1个符号位,5个指数位,10个尾数位
float32 单精度浮点数,包括1个符号位,8个指数位,23个尾数位
float64 双精度浮点数,包括1个符号位,11个指数位,52个尾数位
complex_ 复数类型,与complex128相同
complex64 实部和虚部共享32位的数据类型
complex128 同理
str_ 字符串类型
string_ 字节串类型
datatime64 日期时间类型
timedelta64 两个时间之间的间隔

每一种数据类型否有对应的数据转换方法

np.int8(3.14)
np.float64(1)
np.float(True)



4.3创建数组



4.3.1 array( )方法


np.array(object,dtype,copy,order,subok,ndim)

  • object:任何具有数组接口方法的对象
  • dtype:数据类型
  • copy:默认为True
  • order:元素在内存中出现的顺序,值为K、A、C、F。如果 object 不是数组,则新创建的数组会按照行(C);如果值为F,按照列;如果是数组,则以下成立:C、F、A(原顺序)、K(元素在内存中出现的顺序)
  • subok:布尔类型,True:传递子类,否则返回的数组将强制为基类数组
  • ndim:指定生成数组的最小维数
# 创建一维和二维数组
import numpy as np

n1 = np.array([1, 2, 3])
n2 = np.array([[1, 2, 3], [4, 5, 6]])


# 创建浮点类型数组
import numpy as np

list1 = [1, 2, 3, 4, 5]
n1 = np.array(list1, dtype=np.float_)

print(n1)
print(n1.dtype)
print(type(n1), type(n1[0]))
******************************
运行结果:
[1. 2. 3. 4. 5.]
float64
<class 'numpy.ndarray'> <class 'numpy.float64'>
# 创建三维数组
import numpy as np
nd1 = [1, 2, 3, 4, 5]
np = np.array(nd1,ndmin=3)
print(np)
print(np.shape)
print(np.ndim)
*********************
[[[1 2 3 4 5]]]
(1, 1, 5)
3

  • ndarray.shape

    – 数组的维度。这是一个整数的元组,表示每个维度中数组的大小。对于有

    n

    行和

    m

    列的矩阵,

    shape

    将是

    (n,m)

    。因此,

    shape

    元组的长度就是rank或维度的个数

    ndim



4.3.2 empty( )方法
# 创建指定维度和数据类型的未初始化数组

import numpy as np
n = np.empty([3, 2], dtype=int)
print(n)
*******************
[[1 2]
 [3 4]
 [5 6]]


4.3.3 创建用0填充的数组

zeros( )

import numpy as np
n = np.zeros([3, 2], dtype=int)
print(n)


4.3.4 创建用1填充的数组
import numpy as np
n = np.ones([3, 2], dtype=int)
print(n)


4.3.5 创建随机数组
n = np.random.randint(low,high,size)
  • low:随机数最小值
  • high:可选参数,随机数最大值。如果没有,取值范围(0,low)
  • size:可选参数,表示数组维数
import numpy as np
n = np.random.randint(1, 10, size=(3, 2))
n



4.4 操作数组

1.

加减乘除运算

import numpy as np
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = np.array([[1, 2, 3], [4, 5, 6]])

print(n1 + n2)
print(n1 - n2)
print(n1 * n2)
print(n1 / n2)
************************
[[ 2  4  6]
 [ 8 10 12]]
[[0 0 0]
 [0 0 0]]
[[ 1  4  9]
 [16 25 36]]
[[1. 1. 1.]
 [1. 1. 1.]]


2.比较运算

import numpy as np
n1 = np.array([4,5,6])
n2 = np.array([1,2,3])
print(n1 >= n2)
print(n1 == n2)
print(n1 <= n2)
print(n1 != n2)
************************
[ True  True  True]
[False False False]
[False False False]
[ True  True  True]


3.复制数组

numpy提供的array( )方法里有copy参数,但是更常用的是

n2 = n1.copy()

。修改n2不会影响n1.

import numpy as np
n1 = np.array([[1, 2, 3], [4, 5, 6]])
n2 = n1.copy()
print(n1 == n2)
n2[0][1] = 9
print(n2)
********************
[[ True  True  True]
 [ True  True  True]]
[[1 9 3]
 [4 5 6]]



4.5 数组的索引和切片



4.5.1 索引

所谓数组的索引,就是用于标记数组对应元素的唯一数字。从0开始,即数组中的第一个元素索引是0,以此类推。Numpy支持可以使用标准Python语法

x[obj]

的语法对数组进行索引,其中x是数组,obj是选择方式。

import numpy as np

n1 = np.array([1, 2, 3])
print(n1[1])
********************
2


4.5.2 切片式索引

和Python列表的索引类似。

import numpy as np

nd1 = [1, 2, 3, 4, 5]
n1 = np.array(nd1)
print(n1)
print(n1[1])
**************
2


4.5.3 二维数组索引

二维数组的索引可以使用array[n,m],表示第n个数组的第m个元素。

import numpy as np

nd1 = [1, 2, 3, 4, 5]
n1 = np.array([nd1, nd1])

print(n1, end="\t")
print(n1[1], end="\n")
print(n1[1, 2])
print(n1[1][2])
*************
[[1 2 3 4 5]
 [1 2 3 4 5]]	[1 2 3 4 5]
3
3
# 二维数组切片式索引

image.png



4.5.4 创建图像

在OpenCV中黑白图像是二维数组,彩色图像是三维数组。数组中每个元素就是图像对应位置的像素值。因此修改图像像素其实就是修改数组。

数组索引、像素索引、像素坐标关系:

​ 数组行索引 = 像素所在行数 – 1 = 像素纵坐标

​ 数组列索引 = 像素所在列数 – 1 = 像素横坐标



版权声明:本文为weixin_43547341原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。